PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 line
9.1KB

  1. /* Teensy 4.x, 3.x, LC ADC library
  2. * https://github.com/pedvide/ADC
  3. * Copyright (c) 2020 Pedro Villanueva
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining
  6. * a copy of this software and associated documentation files (the
  7. * "Software"), to deal in the Software without restriction, including
  8. * without limitation the rights to use, copy, modify, merge, publish,
  9. * distribute, sublicense, and/or sell copies of the Software, and to
  10. * permit persons to whom the Software is furnished to do so, subject to
  11. * the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be
  14. * included in all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  20. * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  21. * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE.
  24. */
  25. #ifndef ADC_ATOMIC_H
  26. #define ADC_ATOMIC_H
  27. /* int __builtin_ctz (unsigned int x):
  28. Returns the number of trailing 0-bits in x,
  29. starting at the least significant bit position.
  30. If x is 0, the result is undefined.
  31. */
  32. /* int __builtin_clz (unsigned int x)
  33. Returns the number of leading 0-bits in x,
  34. starting at the most significant bit position.
  35. If x is 0, the result is undefined.
  36. */
  37. /* int __builtin_popcount (unsigned int x)
  38. Returns the number of 1-bits in x.
  39. */
  40. // kinetis.h has the following types for addresses: uint32_t, uint16_t, uint8_t, int32_t, int16_t
  41. //! Atomic set, clear, change, or get bit in a register
  42. namespace atomic
  43. {
  44. /////// Atomic bit set/clear
  45. /* Clear bit in address (make it zero), set bit (make it one), or return the value of that bit
  46. * changeBitFlag can change up to 2 bits in a flag at the same time
  47. * We can change this functions depending on the board.
  48. * Teensy 3.x use bitband while Teensy LC has a more advanced bit manipulation engine.
  49. * Teensy 4 also has bitband capabilities, but are not yet implemented, instead registers are
  50. * set and cleared manually. TODO: fix this.
  51. */
  52. #if defined(KINETISK) // Teensy 3.x
  53. //! Bitband address
  54. /** Gets the aliased address of the bit-band register
  55. * \param reg Register in the bit-band area
  56. * \param bit Bit number of reg to read/modify
  57. * \return A pointer to the aliased address of the bit of reg
  58. */
  59. template <typename T>
  60. __attribute__((always_inline)) inline volatile T &bitband_address(volatile T &reg, uint8_t bit)
  61. {
  62. return (*(volatile T *)(((uint32_t)&reg - 0x40000000) * 32 + bit * 4 + 0x42000000));
  63. }
  64. template <typename T>
  65. __attribute__((always_inline)) inline void setBit(volatile T &reg, uint8_t bit)
  66. {
  67. bitband_address(reg, bit) = 1;
  68. }
  69. template <typename T>
  70. __attribute__((always_inline)) inline void setBitFlag(volatile T &reg, T flag)
  71. {
  72. // 31-__builtin_clzl(flag) = gets bit number in flag
  73. // __builtin_clzl works for long ints, which are guaranteed by standard to be at least 32 bit wide.
  74. // there's no difference in the asm emitted.
  75. bitband_address(reg, 31 - __builtin_clzl(flag)) = 1;
  76. if (__builtin_popcount(flag) > 1)
  77. {
  78. // __builtin_ctzl returns the number of trailing 0-bits in x, starting at the least significant bit position
  79. bitband_address(reg, __builtin_ctzl(flag)) = 1;
  80. }
  81. }
  82. template <typename T>
  83. __attribute__((always_inline)) inline void clearBit(volatile T &reg, uint8_t bit)
  84. {
  85. bitband_address(reg, bit) = 0;
  86. }
  87. template <typename T>
  88. __attribute__((always_inline)) inline void clearBitFlag(volatile T &reg, T flag)
  89. {
  90. bitband_address(reg, 31 - __builtin_clzl(flag)) = 0;
  91. if (__builtin_popcount(flag) > 1)
  92. {
  93. bitband_address(reg, __builtin_ctzl(flag)) = 0;
  94. }
  95. }
  96. template <typename T>
  97. __attribute__((always_inline)) inline void changeBit(volatile T &reg, uint8_t bit, bool state)
  98. {
  99. bitband_address(reg, bit) = state;
  100. }
  101. template <typename T>
  102. __attribute__((always_inline)) inline void changeBitFlag(volatile T &reg, T flag, T state)
  103. {
  104. bitband_address(reg, __builtin_ctzl(flag)) = (state >> __builtin_ctzl(flag)) & 0x1;
  105. if (__builtin_popcount(flag) > 1)
  106. {
  107. bitband_address(reg, 31 - __builtin_clzl(flag)) = (state >> (31 - __builtin_clzl(flag))) & 0x1;
  108. }
  109. }
  110. template <typename T>
  111. __attribute__((always_inline)) inline volatile bool getBit(volatile T &reg, uint8_t bit)
  112. {
  113. return (volatile bool)bitband_address(reg, bit);
  114. }
  115. template <typename T>
  116. __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T &reg, T flag)
  117. {
  118. return (volatile bool)bitband_address(reg, 31 - __builtin_clzl(flag));
  119. }
  120. #elif defined(__IMXRT1062__) // Teensy 4
  121. template <typename T>
  122. __attribute__((always_inline)) inline void setBitFlag(volatile T &reg, T flag)
  123. {
  124. __disable_irq();
  125. reg |= flag;
  126. __enable_irq();
  127. }
  128. template <typename T>
  129. __attribute__((always_inline)) inline void clearBitFlag(volatile T &reg, T flag)
  130. {
  131. __disable_irq();
  132. reg &= ~flag;
  133. __enable_irq();
  134. }
  135. template <typename T>
  136. __attribute__((always_inline)) inline void changeBitFlag(volatile T &reg, T flag, T state)
  137. {
  138. // flag can be 1 or 2 bits wide
  139. // state can have one or two bits set
  140. if (__builtin_popcount(flag) == 1)
  141. { // 1 bit
  142. if (state)
  143. {
  144. setBitFlag(reg, flag);
  145. }
  146. else
  147. {
  148. clearBitFlag(reg, flag);
  149. }
  150. }
  151. else
  152. { // 2 bits
  153. // lsb first
  154. if ((state >> __builtin_ctzl(flag)) & 0x1)
  155. { // lsb of state is 1
  156. setBitFlag(reg, (uint32_t)(1 << __builtin_ctzl(flag)));
  157. }
  158. else
  159. { // lsb is 0
  160. clearBitFlag(reg, (uint32_t)(1 << __builtin_ctzl(flag)));
  161. }
  162. // msb
  163. if ((state >> (31 - __builtin_clzl(flag))) & 0x1)
  164. { // msb of state is 1
  165. setBitFlag(reg, (uint32_t)(1 << (31 - __builtin_clzl(flag))));
  166. }
  167. else
  168. { // msb is 0
  169. clearBitFlag(reg, (uint32_t)(1 << (31 - __builtin_clzl(flag))));
  170. }
  171. }
  172. }
  173. template <typename T>
  174. __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T &reg, T flag)
  175. {
  176. return (volatile bool)((reg)&flag) >> (31 - __builtin_clzl(flag));
  177. }
  178. #elif defined(KINETISL) // Teensy LC
  179. // bit manipulation engine
  180. template <typename T>
  181. __attribute__((always_inline)) inline void setBit(volatile T &reg, uint8_t bit)
  182. {
  183. //temp = *(uint32_t *)((uint32_t)(reg) | (1<<26) | (bit<<21)); // LAS
  184. *(volatile T *)((uint32_t)(&reg) | (1 << 27)) = 1 << bit; // OR
  185. }
  186. template <typename T>
  187. __attribute__((always_inline)) inline void setBitFlag(volatile T &reg, uint32_t flag)
  188. {
  189. *(volatile T *)((uint32_t)&reg | (1 << 27)) = flag; // OR
  190. }
  191. template <typename T>
  192. __attribute__((always_inline)) inline void clearBit(volatile T &reg, uint8_t bit)
  193. {
  194. //temp = *(uint32_t *)((uint32_t)(reg) | (3<<27) | (bit<<21)); // LAC
  195. *(volatile T *)((uint32_t)(&reg) | (1 << 26)) = ~(1 << bit); // AND
  196. }
  197. template <typename T>
  198. __attribute__((always_inline)) inline void clearBitFlag(volatile T &reg, uint32_t flag)
  199. {
  200. //temp = *(uint32_t *)((uint32_t)(reg) | (3<<27) | (bit<<21)); // LAC
  201. *(volatile T *)((uint32_t)(&reg) | (1 << 26)) = ~flag; // AND
  202. }
  203. template <typename T>
  204. __attribute__((always_inline)) inline void changeBit(volatile T &reg, uint8_t bit, bool state)
  205. {
  206. //temp = *(uint32_t *)((uint32_t)(reg) | ((3-2*!!state)<<27) | (bit<<21)); // LAS/LAC
  207. state ? setBit(reg, bit) : clearBit(reg, bit);
  208. }
  209. template <typename T>
  210. __attribute__((always_inline)) inline void changeBitFlag(volatile T &reg, T flag, T state)
  211. {
  212. // BFI, bitfield width set to __builtin_popcount(flag)
  213. // least significant bit set to __builtin_ctzl(flag)
  214. *(volatile T *)((uint32_t)(&reg) | (1 << 28) | (__builtin_ctzl(flag) << 23) | ((__builtin_popcount(flag) - 1) << 19)) = state;
  215. }
  216. template <typename T>
  217. __attribute__((always_inline)) inline volatile bool getBit(volatile T &reg, uint8_t bit)
  218. {
  219. return (volatile bool)*(volatile T *)((uint32_t)(&reg) | (1 << 28) | (bit << 23)); // UBFX
  220. }
  221. template <typename T>
  222. __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T &reg, T flag)
  223. {
  224. return (volatile bool)*(volatile T *)((uint32_t)(&reg) | (1 << 28) | ((31 - __builtin_clzl(flag)) << 23)); // UBFX
  225. }
  226. #endif
  227. } // namespace atomic
  228. #endif // ADC_ATOMIC_H