PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

416 linhas
16KB

  1. /*
  2. CapacitiveSense.h - Capacitive Sensing Library for 'duino / Wiring
  3. https://github.com/PaulStoffregen/CapacitiveSensor
  4. http://www.pjrc.com/teensy/td_libs_CapacitiveSensor.html
  5. http://playground.arduino.cc/Main/CapacitiveSensor
  6. Copyright (c) 2009 Paul Bagder
  7. Updates for other hardare by Paul Stoffregen, 2010-2016
  8. vim: set ts=4:
  9. Permission is hereby granted, free of charge, to any person obtaining a
  10. copy of this software and associated documentation files (the "Software"),
  11. to deal in the Software without restriction, including without limitation
  12. the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13. and/or sell copies of the Software, and to permit persons to whom the
  14. Software is furnished to do so, subject to the following conditions:
  15. The above copyright notice and this permission notice shall be included in
  16. all copies or substantial portions of the Software.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  22. FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  23. DEALINGS IN THE SOFTWARE.
  24. */
  25. // ensure this library description is only included once
  26. #ifndef CapacitiveSensor_h
  27. #define CapacitiveSensor_h
  28. #if ARDUINO >= 100
  29. #include "Arduino.h"
  30. #else
  31. #include "WProgram.h"
  32. #endif
  33. // Direct I/O through registers and bitmask (from OneWire library)
  34. #if defined(__AVR__)
  35. #define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin)))
  36. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  37. #define IO_REG_TYPE uint8_t
  38. #define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0)
  39. #define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask), (*((base)+2)) &= ~(mask))
  40. #define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask))
  41. #define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask))
  42. #define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask))
  43. #elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
  44. #define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
  45. #define PIN_TO_BITMASK(pin) (1)
  46. #define IO_REG_TYPE uint8_t
  47. #define IO_REG_ASM
  48. #define DIRECT_READ(base, mask) (*((base)+512))
  49. #define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0)
  50. #define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1)
  51. #define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1)
  52. #define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1)
  53. #elif defined(__MKL26Z64__)
  54. #define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
  55. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  56. #define IO_REG_TYPE uint8_t
  57. #define IO_REG_ASM
  58. #define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0)
  59. #define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask))
  60. #define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask))
  61. #define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask))
  62. #define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask))
  63. #elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
  64. #define PIN_TO_BASEREG(pin) (portOutputRegister(pin))
  65. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  66. #define IO_REG_TYPE uint32_t
  67. #define IO_REG_ASM
  68. #define DIRECT_READ(base, mask) ((*((base)+2) & (mask)) ? 1 : 0)
  69. #define DIRECT_MODE_INPUT(base, mask) (*((base)+1) &= ~(mask))
  70. #define DIRECT_MODE_OUTPUT(base, mask) (*((base)+1) |= (mask))
  71. #define DIRECT_WRITE_LOW(base, mask) (*((base)+34) = (mask))
  72. #define DIRECT_WRITE_HIGH(base, mask) (*((base)+33) = (mask))
  73. #elif defined(__SAM3X8E__)
  74. #define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER))
  75. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  76. #define IO_REG_TYPE uint32_t
  77. #define IO_REG_ASM
  78. #define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0)
  79. #define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask))
  80. #define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask))
  81. #define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask))
  82. #define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask))
  83. #elif defined(__PIC32MX__)
  84. #define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin)))
  85. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  86. #define IO_REG_TYPE uint32_t
  87. #define IO_REG_ASM
  88. #define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10
  89. #define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08
  90. #define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04
  91. #define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24
  92. #define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28
  93. #elif defined(ARDUINO_ARCH_ESP8266)
  94. #define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO)
  95. #define PIN_TO_BITMASK(pin) (1 << pin)
  96. #define IO_REG_TYPE uint32_t
  97. #define IO_REG_ASM
  98. #define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS
  99. #define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS
  100. #define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS
  101. #define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS
  102. #define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS
  103. #elif defined(ARDUINO_ARCH_ESP32)
  104. #include <driver/rtc_io.h>
  105. #define PIN_TO_BASEREG(pin) (0)
  106. #define PIN_TO_BITMASK(pin) (pin)
  107. #define IO_REG_TYPE uint32_t
  108. #define IO_REG_BASE_ATTR
  109. #define IO_REG_MASK_ATTR
  110. static inline __attribute__((always_inline))
  111. IO_REG_TYPE directRead(IO_REG_TYPE pin)
  112. {
  113. if ( pin < 32 )
  114. return (GPIO.in >> pin) & 0x1;
  115. else if ( pin < 40 )
  116. return (GPIO.in1.val >> (pin - 32)) & 0x1;
  117. return 0;
  118. }
  119. static inline __attribute__((always_inline))
  120. void directWriteLow(IO_REG_TYPE pin)
  121. {
  122. if ( pin < 32 )
  123. GPIO.out_w1tc = ((uint32_t)1 << pin);
  124. else if ( pin < 34 )
  125. GPIO.out1_w1tc.val = ((uint32_t)1 << (pin - 32));
  126. }
  127. static inline __attribute__((always_inline))
  128. void directWriteHigh(IO_REG_TYPE pin)
  129. {
  130. if ( pin < 32 )
  131. GPIO.out_w1ts = ((uint32_t)1 << pin);
  132. else if ( pin < 34 )
  133. GPIO.out1_w1ts.val = ((uint32_t)1 << (pin - 32));
  134. }
  135. static inline __attribute__((always_inline))
  136. void directModeInput(IO_REG_TYPE pin)
  137. {
  138. if ( digitalPinIsValid(pin) )
  139. {
  140. uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
  141. if ( rtc_reg ) // RTC pins PULL settings
  142. {
  143. ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
  144. ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
  145. }
  146. if ( pin < 32 )
  147. GPIO.enable_w1tc = ((uint32_t)1 << pin);
  148. else
  149. GPIO.enable1_w1tc.val = ((uint32_t)1 << (pin - 32));
  150. uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
  151. pinFunction |= FUN_IE; // input enable but required for output as well?
  152. pinFunction |= ((uint32_t)2 << MCU_SEL_S);
  153. ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
  154. GPIO.pin[pin].val = 0;
  155. }
  156. }
  157. static inline __attribute__((always_inline))
  158. void directModeOutput(IO_REG_TYPE pin)
  159. {
  160. if ( digitalPinIsValid(pin) && pin <= 33 ) // pins above 33 can be only inputs
  161. {
  162. uint32_t rtc_reg(rtc_gpio_desc[pin].reg);
  163. if ( rtc_reg ) // RTC pins PULL settings
  164. {
  165. ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].mux);
  166. ESP_REG(rtc_reg) = ESP_REG(rtc_reg) & ~(rtc_gpio_desc[pin].pullup | rtc_gpio_desc[pin].pulldown);
  167. }
  168. if ( pin < 32 )
  169. GPIO.enable_w1ts = ((uint32_t)1 << pin);
  170. else // already validated to pins <= 33
  171. GPIO.enable1_w1ts.val = ((uint32_t)1 << (pin - 32));
  172. uint32_t pinFunction((uint32_t)2 << FUN_DRV_S); // what are the drivers?
  173. pinFunction |= FUN_IE; // input enable but required for output as well?
  174. pinFunction |= ((uint32_t)2 << MCU_SEL_S);
  175. ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) = pinFunction;
  176. GPIO.pin[pin].val = 0;
  177. }
  178. }
  179. #define DIRECT_READ(base, pin) directRead(pin)
  180. #define DIRECT_WRITE_LOW(base, pin) directWriteLow(pin)
  181. #define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(pin)
  182. #define DIRECT_MODE_INPUT(base, pin) directModeInput(pin)
  183. #define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(pin)
  184. // https://github.com/PaulStoffregen/OneWire/pull/47
  185. // https://github.com/stickbreaker/OneWire/commit/6eb7fc1c11a15b6ac8c60e5671cf36eb6829f82c
  186. #ifdef interrupts
  187. #undef interrupts
  188. #endif
  189. #ifdef noInterrupts
  190. #undef noInterrupts
  191. #endif
  192. #define noInterrupts() {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;portENTER_CRITICAL(&mux)
  193. #define interrupts() portEXIT_CRITICAL(&mux);}
  194. //#warning, code is copied from "ESP32 OneWire testing"
  195. #elif defined(__SAMD21G18A__)
  196. // runs extremely slow/unreliable on Arduino Zero - help wanted....
  197. #define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin))
  198. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  199. #define IO_REG_TYPE uint32_t
  200. #define IO_REG_ASM
  201. #define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0)
  202. #define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask))
  203. #define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask))
  204. #define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask))
  205. #define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask))
  206. #elif defined(__SAMD51__)
  207. #define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin))
  208. #define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin))
  209. #define IO_REG_TYPE uint32_t
  210. #define IO_REG_ASM
  211. #define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0) // IN
  212. #define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask)) // DIRCLR
  213. #define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask)) // DIRSET
  214. #define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask)) // OUTCLR
  215. #define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask)) /// OUTSET
  216. #elif defined(ARDUINO_NRF52_ADAFRUIT)
  217. #define PIN_TO_BASEREG(pin) (0)
  218. #define PIN_TO_BITMASK(pin) digitalPinToPinName(pin)
  219. #define IO_REG_TYPE uint32_t
  220. #define IO_REG_ASM
  221. #define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin)
  222. #define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin)
  223. #define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin)
  224. #define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL)
  225. #define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin)
  226. #elif defined(RBL_NRF51822)
  227. #define PIN_TO_BASEREG(pin) (0)
  228. #define PIN_TO_BITMASK(pin) (pin)
  229. #define IO_REG_TYPE uint32_t
  230. #define IO_REG_ASM
  231. #define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin)
  232. #define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin)
  233. #define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin)
  234. #define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL)
  235. #define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin)
  236. #elif defined(__arc__)
  237. #include "scss_registers.h"
  238. #include "portable.h"
  239. #include "avr/pgmspace.h"
  240. #define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId)
  241. #define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType)
  242. #define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase)
  243. #define DIR_OFFSET_SS 0x01
  244. #define DIR_OFFSET_SOC 0x04
  245. #define EXT_PORT_OFFSET_SS 0x0A
  246. #define EXT_PORT_OFFSET_SOC 0x50
  247. /* GPIO registers base address */
  248. #define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase)
  249. #define PIN_TO_BITMASK(pin) pin
  250. #define IO_REG_TYPE uint32_t
  251. #define IO_REG_ASM
  252. static inline __attribute__((always_inline))
  253. IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
  254. {
  255. IO_REG_TYPE ret;
  256. if (SS_GPIO == GPIO_TYPE(pin)) {
  257. ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS));
  258. } else {
  259. ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC);
  260. }
  261. return ((ret >> GPIO_ID(pin)) & 0x01);
  262. }
  263. static inline __attribute__((always_inline))
  264. void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
  265. {
  266. if (SS_GPIO == GPIO_TYPE(pin)) {
  267. WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)),
  268. ((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
  269. } else {
  270. MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin));
  271. }
  272. }
  273. static inline __attribute__((always_inline))
  274. void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
  275. {
  276. if (SS_GPIO == GPIO_TYPE(pin)) {
  277. WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)),
  278. ((IO_REG_TYPE)(base) + DIR_OFFSET_SS));
  279. } else {
  280. MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin));
  281. }
  282. }
  283. static inline __attribute__((always_inline))
  284. void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
  285. {
  286. if (SS_GPIO == GPIO_TYPE(pin)) {
  287. WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base);
  288. } else {
  289. MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin));
  290. }
  291. }
  292. static inline __attribute__((always_inline))
  293. void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin)
  294. {
  295. if (SS_GPIO == GPIO_TYPE(pin)) {
  296. WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base);
  297. } else {
  298. MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin));
  299. }
  300. }
  301. #define DIRECT_READ(base, pin) directRead(base, pin)
  302. #define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin)
  303. #define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin)
  304. #define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin)
  305. #define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin)
  306. #elif defined(ARDUINO_ARCH_STM32)
  307. #define PIN_TO_BASEREG(pin) (0)
  308. #define PIN_TO_BITMASK(pin) (pin)
  309. #define IO_REG_TYPE uint32_t
  310. #define IO_REG_ASM
  311. #define DIRECT_READ(base, pin) digitalRead(pin)
  312. #define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT)
  313. #define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT)
  314. #define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW)
  315. #define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH)
  316. #elif defined(ARDUINO_ARCH_APOLLO3)
  317. #define PIN_TO_BASEREG(pin) (0)
  318. #define PIN_TO_BITMASK(pin) (pin)
  319. #define IO_REG_TYPE uint32_t
  320. #define IO_REG_ASM
  321. #define DIRECT_READ(base, mask) (am_hal_gpio_input_read(mask))
  322. #define DIRECT_MODE_INPUT(base, mask) (am_hal_gpio_pinconfig(mask, g_AM_HAL_GPIO_INPUT))
  323. #define DIRECT_MODE_OUTPUT(base, mask) (am_hal_gpio_pinconfig(mask, g_AM_HAL_GPIO_OUTPUT))
  324. #define DIRECT_WRITE_LOW(base, mask) (am_hal_gpio_output_clear(mask))
  325. #define DIRECT_WRITE_HIGH(base, mask) (am_hal_gpio_output_set(mask))
  326. #endif
  327. // some 3.3V chips with 5V tolerant pins need this workaround
  328. //
  329. #if defined(__MK20DX256__)
  330. #define FIVE_VOLT_TOLERANCE_WORKAROUND
  331. #endif
  332. // library interface description
  333. class CapacitiveSensor
  334. {
  335. // user-accessible "public" interface
  336. public:
  337. // methods
  338. CapacitiveSensor(uint8_t sendPin, uint8_t receivePin);
  339. long capacitiveSensorRaw(uint8_t samples);
  340. long capacitiveSensor(uint8_t samples);
  341. void set_CS_Timeout_Millis(unsigned long timeout_millis);
  342. void reset_CS_AutoCal();
  343. void set_CS_AutocaL_Millis(unsigned long autoCal_millis);
  344. // library-accessible "private" interface
  345. private:
  346. // variables
  347. int error;
  348. unsigned long leastTotal;
  349. unsigned int loopTimingFactor;
  350. unsigned long CS_Timeout_Millis;
  351. unsigned long CS_AutocaL_Millis;
  352. unsigned long lastCal;
  353. unsigned long total;
  354. IO_REG_TYPE sBit; // send pin's ports and bitmask
  355. volatile IO_REG_TYPE *sReg;
  356. IO_REG_TYPE rBit; // receive pin's ports and bitmask
  357. volatile IO_REG_TYPE *rReg;
  358. // methods
  359. int SenseOneCycle(void);
  360. };
  361. #endif