Guest User

Untitled

a guest
Jun 29th, 2016
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.72 KB | None | 0 0
  1. // IGEP v2 pwm-demo rev.F --- Demonstrate usage of the dm3730-pwm library
  2. // Based on the omap3730-pwm-demo by Mark A. Yoder
  3. // Modified by Pavel Kopylov 29-June-2016
  4. // Copyright (c) 2010 Thomas W. Most <twm@freecog.net>
  5. //
  6. // The contents of this file may be used subject to the terms of either of the
  7. // following licenses:
  8. //
  9. // GNU LGPL 2.1 license:
  10. //
  11. // This library is free software; you can redistribute it and/or modify it
  12. // under the terms of the GNU Lesser General Public License as published by the
  13. // Free Software Foundation; either version 2.1 of the License, or (at your
  14. // option) any later version.
  15. //
  16. // This library is distributed in the hope that it will be useful, but WITHOUT
  17. // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  18. // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
  19. // for more details.
  20. //
  21. // You should have received a copy of the GNU Lesser General Public License
  22. // along with this library; if not, write to the Free Software Foundation,
  23. // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24. //
  25. // MIT license:
  26. //
  27. // Permission is hereby granted, free of charge, to any person obtaining a copy
  28. // of this software and associated documentation files (the "Software"), to deal
  29. // in the Software without restriction, including without limitation the rights
  30. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  31. // copies of the Software, and to permit persons to whom the Software is
  32. // furnished to do so, subject to the following conditions:
  33. //
  34. // The above copyright notice and this permission notice shall be included in
  35. // all copies or substantial portions of the Software.
  36. //
  37. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  38. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  39. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  40. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  41. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  42. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  43. // THE SOFTWARE.
  44.  
  45. #include <glib.h>
  46. #include <unistd.h>
  47. #include <stdio.h>
  48. #include <fcntl.h>
  49. #include <sys/types.h>
  50. #include <sys/stat.h>
  51. #include <sys/mman.h>
  52. #include <errno.h>
  53.  
  54. #include "dm3730-pwm.h"
  55.  
  56. // Clock configuration registers (TRM p. 470)
  57. #define CM_FCLKEN_PER 0x48005000
  58. #define FCLKEN_GPT8_MASK (1 << 9)
  59.  
  60. #define CM_ICLKEN_PER 0x48005010
  61. #define ICLKEN_GPT8_MASK (1 << 9)
  62.  
  63. #define CM_IDLEST_PER 0x48005020
  64. #define IDLEST_GPT8_MASK (1 << 9)
  65.  
  66. #define CM_AUTOIDLE_PER 0x48005030
  67. #define AUTOIDLE_GPT8_MASK (1 << 9)
  68.  
  69. #define CM_CLKSEL_PER 0x48005040
  70. #define CLKSEL_GPT8_MASK (1 << 6)
  71.  
  72. // GPTIMER register offsets
  73. #define GPTIMER8 0x4903E000
  74. #define GPT_REG_TCLR 0x024
  75. #define GPT_REG_TCRR 0x028
  76. #define GPT_REG_TLDR 0x02c
  77. #define GPT_REG_TMAR 0x038
  78.  
  79. // Get a guint32 pointer to the register in block `instance` at byte
  80. // offset `offset`.
  81. #define REG32_PTR(instance, offset) ((volatile guint32*) (instance + offset))
  82.  
  83.  
  84. // The default Linux page size is 4k and the GP timer register
  85. // blocks are aligned to 4k. Therefore it is convenient to just
  86. // assume that pages are aligned there for the purposes of mmap()
  87. // (since mmap only maps aligned pages). This function checks
  88. // that assumption and aborts if it is untrue.
  89. static void check_pagesize(void)
  90. {
  91. if (getpagesize() != 4096)
  92. {
  93. g_error("The page size is %d. Must be 4096.", getpagesize());
  94. }
  95. }
  96.  
  97. // Simply a wrapper around mmap that passes the correct arguments
  98. // for mapping a register block. `instance_number` must be between
  99. // 1 and 12, or errno will be set to EDOM and MAP_FAILED returned.
  100. // Otherwise the return value is that of `mmap()`.
  101. guint8* pwm_mmap_instance(int mem_fd)
  102. {
  103. guint8* instance;
  104.  
  105.  
  106. instance = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, GPTIMER8);
  107. printf("%s: 0x%08X mmaped --> 0x%08X\n", __func__, GPTIMER8, (guint32)instance);
  108.  
  109. return instance;
  110. }
  111.  
  112. // The inverse of `pwm_mmap_instance()`, this is simply a wrapper
  113. // arount `munmap()`. It returns the underlying `munmap()` call's
  114. // return value.
  115. int pwm_munmap_instance(guint8 *instance)
  116. {
  117. return munmap(instance, 4096);
  118. }
  119.  
  120. // Configure the clocks for GPTIMER8, which can be set to
  121. // use the 13 MHz system clock (otherwise they use the 32 kHz clock like
  122. // the rest of the timers). Return -1 on failure, with errno set.
  123. int pwm_config_clock(int mem_fd, gboolean gptimer_13mhz)
  124. {
  125. int page_addr = CM_FCLKEN_PER & 0xfffff000;
  126. int offset_FCLKEN = CM_FCLKEN_PER & 0xfff;
  127. int offset_ICLKEN = CM_ICLKEN_PER & 0xfff;
  128. int offset_IDLEST = CM_IDLEST_PER & 0xfff;
  129. int offset_AUTOIDLE = CM_AUTOIDLE_PER & 0xfff;
  130. int offset_CLKSEL = CM_CLKSEL_PER & 0xfff;
  131. guint32 value;
  132.  
  133.  
  134. printf("%s\n", __func__);
  135. guint8 *registers = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, page_addr);
  136. if (registers == MAP_FAILED)
  137. {
  138. return -1;
  139. }
  140. printf("%s: 0x%08X mmaped --> 0x%08X\n", __func__, page_addr, (guint32)registers);
  141.  
  142. value = *REG32_PTR(registers, offset_FCLKEN);
  143. printf("%s(0x%08X): CM_FCLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
  144. __func__, (guint32)registers + offset_FCLKEN, value, value & FCLKEN_GPT8_MASK ? 1 : 0);
  145.  
  146. value = *REG32_PTR(registers, offset_ICLKEN);
  147. printf("%s(0x%08X): CM_ICLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
  148. __func__, (guint32)registers + offset_ICLKEN, value, value & ICLKEN_GPT8_MASK ? 1 : 0);
  149.  
  150. value = *REG32_PTR(registers, offset_IDLEST);
  151. printf("%s(0x%08X): CM_IDLEST_PER = 0x%08X, ST_GPT8 = %d\n",
  152. __func__, (guint32)registers + offset_IDLEST, value, value & IDLEST_GPT8_MASK ? 1 : 0);
  153.  
  154. value = *REG32_PTR(registers, offset_AUTOIDLE);
  155. printf("%s(0x%08X): CM_AUTOIDLE_PER = 0x%08X, AUTO_GPT8 = %d\n",
  156. __func__, (guint32)registers + offset_AUTOIDLE, value, value & AUTOIDLE_GPT8_MASK ? 1 : 0);
  157.  
  158. value = *REG32_PTR(registers, offset_CLKSEL);
  159. printf("%s(0x%08X): CM_CLKSEL_PER = 0x%08X, CLKSEL_GPT8 = %d\n",
  160. __func__, (guint32)registers + offset_CLKSEL, value, value & CLKSEL_GPT8_MASK ? 1 : 0);
  161.  
  162. /* -------------------------- */
  163.  
  164. value = *REG32_PTR(registers, offset_FCLKEN);
  165. value |= FCLKEN_GPT8_MASK;
  166. *REG32_PTR(registers, offset_FCLKEN) = value;
  167.  
  168. value = *REG32_PTR(registers, offset_ICLKEN);
  169. value |= ICLKEN_GPT8_MASK;
  170. *REG32_PTR(registers, offset_ICLKEN) = value;
  171.  
  172. value = *REG32_PTR(registers, offset_CLKSEL);
  173. value &= ~(CLKSEL_GPT8_MASK);
  174. if (gptimer_13mhz) value |= CLKSEL_GPT8_MASK;
  175. *REG32_PTR(registers, offset_CLKSEL) = value;
  176.  
  177. printf("--------------------------\n");
  178.  
  179. value = *REG32_PTR(registers, offset_FCLKEN);
  180. printf("%s(0x%08X): CM_FCLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
  181. __func__, (guint32)registers + offset_FCLKEN, value, value & FCLKEN_GPT8_MASK ? 1 : 0);
  182.  
  183. value = *REG32_PTR(registers, offset_ICLKEN);
  184. printf("%s(0x%08X): CM_ICLKEN_PER = 0x%08X, EN_GPT8 = %d\n",
  185. __func__, (guint32)registers + offset_ICLKEN, value, value & ICLKEN_GPT8_MASK ? 1 : 0);
  186.  
  187. value = *REG32_PTR(registers, offset_IDLEST);
  188. printf("%s(0x%08X): CM_IDLEST_PER = 0x%08X, ST_GPT8 = %d\n",
  189. __func__, (guint32)registers + offset_IDLEST, value, value & IDLEST_GPT8_MASK ? 1 : 0);
  190.  
  191. value = *REG32_PTR(registers, offset_AUTOIDLE);
  192. printf("%s(0x%08X): CM_AUTOIDLE_PER = 0x%08X, AUTO_GPT8 = %d\n",
  193. __func__, (guint32)registers + offset_AUTOIDLE, value, value & AUTOIDLE_GPT8_MASK ? 1 : 0);
  194.  
  195. value = *REG32_PTR(registers, offset_CLKSEL);
  196. printf("%s(0x%08X): CM_CLKSEL_PER = 0x%08X, CLKSEL_GPT8 = %d\n",
  197. __func__, (guint32)registers + offset_CLKSEL, value, value & CLKSEL_GPT8_MASK ? 1 : 0);
  198.  
  199. return munmap(registers, 4096);
  200. }
  201.  
  202. // Calculate the resolution of the PWM (the number of clock ticks
  203. // in the period), which is passed to `pwm_config_timer()`.
  204. guint32 pwm_calc_resolution(int pwm_frequency, int clock_frequency)
  205. {
  206. float pwm_period = 1.0 / pwm_frequency;
  207. float clock_period = 1.0 / clock_frequency;
  208. printf("%s\n", __func__);
  209.  
  210. return (guint32) (pwm_period / clock_period);
  211. }
  212.  
  213. // Initialize the control registers of the specified timer
  214. // instance for PWM at the specified resolution.
  215. void pwm_config_timer(guint8 *registers, guint32 resolution, float duty_cycle)
  216. {
  217. guint32 counter_start = 0xffffffff - resolution;
  218. guint32 dc = 0xffffffff - ((guint32) (resolution * duty_cycle));
  219. guint32 value;
  220.  
  221.  
  222. // Edge condition: the duty cycle is set within two units of the overflow
  223. // value. Loading the register with this value shouldn't be done (TRM 16.2.4.6).
  224. if (0xffffffff - dc <= 2)
  225. {
  226. dc = 0xffffffff - 2;
  227. }
  228.  
  229. // Edge condition: TMAR will be set to within two units of the overflow
  230. // value. This means that the resolution is extremely low, which doesn't
  231. // really make sense, but whatever.
  232. if (0xffffffff - counter_start <= 2)
  233. {
  234. counter_start = 0xffffffff - 2;
  235. }
  236.  
  237. *REG32_PTR(registers, GPT_REG_TCLR) = 0; // Turn off
  238. value = *REG32_PTR(registers, GPT_REG_TCLR);
  239. printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCLR, value);
  240.  
  241. *REG32_PTR(registers, GPT_REG_TCRR) = counter_start;
  242. value = *REG32_PTR(registers, GPT_REG_TCRR);
  243. printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCRR, value);
  244.  
  245. *REG32_PTR(registers, GPT_REG_TLDR) = counter_start;
  246. value = *REG32_PTR(registers, GPT_REG_TLDR);
  247. printf("%s: GPT_REG_TLDR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TLDR, value);
  248.  
  249. *REG32_PTR(registers, GPT_REG_TMAR) = dc;
  250. value = *REG32_PTR(registers, GPT_REG_TMAR);
  251. printf("%s: GPT_REG_TMAR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TMAR, value);
  252.  
  253. *REG32_PTR(registers, GPT_REG_TCLR) = (
  254. (1 << 0) | // ST -- enable counter
  255. (1 << 1) | // AR -- autoreload on overflow
  256. (1 << 6) | // CE -- compare enabled
  257. (1 << 7) | // SCPWM -- invert pulse
  258. (2 << 10) | // TRG -- overflow and match trigger
  259. (1 << 12) // PT -- toggle PWM mode
  260. );
  261. value = *REG32_PTR(registers, GPT_REG_TCLR);
  262. printf("%s: GPT_REG_TCLR (0x%08X) = 0x%08X\n", __func__, (guint32)registers + GPT_REG_TCLR, value);
  263. }
  264.  
  265. int pwm_open_devmem(void)
  266. {
  267. check_pagesize();
  268.  
  269. return open("/dev/mem", O_RDWR | O_SYNC);
  270. }
  271.  
  272. void pwm_close_devmem(int dev_fd)
  273. {
  274. /* This function is useful! */
  275. close(dev_fd);
  276. }
Add Comment
Please, Sign In to add comment