main.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. /*
  2. * Signal debouncer
  3. *
  4. * This code is designed to run on an ATmega8/88 with 20MHz or 16MHz clock.
  5. *
  6. * Copyright (c) 2008 Michael Buesch <mb@bu3sch.de>
  7. *
  8. * Licensed under the GNU General Public License version 2 or later.
  9. */
  10. /* What is DWELL_TIME and what is ACTIVE_TIME?
  11. * Consider we have one input signal and one output signal.
  12. * The timeouts look like this:
  13. *
  14. * ---------------
  15. * | |
  16. * input | |
  17. * ---------- ----------
  18. *
  19. * ---------------
  20. * | |
  21. * output | |
  22. * ---------------- -----
  23. *
  24. * ^--v--^ ^--v--^
  25. * | |
  26. * ACTIVE_TIME DWELL_TIME
  27. *
  28. * So the ACTIVE_TIME is the time for the output to respond to the input
  29. * signal and the DWELL_TIME is the additional dwell time the output stays
  30. * active after the input got deasserted.
  31. * ACTIVE_TIME should be fairly low; in the range of a few microseconds. It's
  32. * used for noise-cancelling.
  33. * Units for ACTIVE_TIME and DWELL_TIME are microseconds.
  34. */
  35. #include "util.h"
  36. #include <stdint.h>
  37. #include <avr/io.h>
  38. #include <avr/interrupt.h>
  39. #include <avr/wdt.h>
  40. #define CPU_HZ MHz(20)
  41. //#define CPU_HZ MHz(16)
  42. #define MHz(hz) (1000000ul * (hz))
  43. /* Compat */
  44. #ifdef MCUCSR
  45. # define MCUSR MCUCSR
  46. #endif
  47. #ifdef TIMSK
  48. # define TIMSK1 TIMSK
  49. #endif
  50. #ifdef TIFR
  51. # define TIFR1 TIFR
  52. #endif
  53. /**
  54. * struct input_pin - An input pin definition
  55. *
  56. * @input_port: The signal input port. PORTB, PORTC, ...
  57. * @input_pin: The signal input pin. PINB, PINC, ...
  58. * @input_ddr: Data direction register for input_port.
  59. * @input_bit: The bit number on the input_port.
  60. * @flags: See enum input_pin_flags.
  61. */
  62. struct input_pin {
  63. uint16_t input_port;
  64. uint16_t input_pin;
  65. uint16_t input_ddr;
  66. uint8_t input_bit;
  67. uint8_t flags;
  68. };
  69. /**
  70. * enum input_pin_flags - Flags for an input pin
  71. *
  72. * @INPUT_PULLUP: Use pullups for the input pin.
  73. * @INPUT_INVERT: Logically invert the input signal.
  74. */
  75. enum input_pin_flags {
  76. INPUT_PULLUP = (1 << 0),
  77. INPUT_INVERT = (1 << 1),
  78. };
  79. /**
  80. * struct output_pin - Level triggered output pin
  81. *
  82. * @output_port: The signal output port. PORTB, PORTC, ...
  83. * @output_ddr: Data direction register for output_port.
  84. * @output_bit: The bit number on the output_port.
  85. * @flags: See enum output_pin_flags.
  86. */
  87. struct output_pin {
  88. uint16_t output_port;
  89. uint16_t output_ddr;
  90. uint8_t output_bit;
  91. uint8_t flags;
  92. /* Trigger level */
  93. uint8_t level;
  94. };
  95. /**
  96. * enum output_pin_flags - Flags for an output pin
  97. *
  98. * @OUTPUT_INVERT: Logically invert the output signal.
  99. */
  100. enum output_pin_flags {
  101. OUTPUT_INVERT = (1 << 0),
  102. };
  103. /**
  104. * struct connection - Logical connection between input and output pins
  105. *
  106. * @in: Definition of the input pin.
  107. * @out: Pointer to the output pin.
  108. */
  109. struct connection {
  110. struct input_pin in;
  111. struct output_pin *out;
  112. bool input_is_asserted;
  113. uint32_t dwell_timeout;
  114. };
  115. #define DEF_INPUT(portid, bit, _flags) \
  116. .in = { \
  117. .input_port = _SFR_ADDR(PORT##portid), \
  118. .input_pin = _SFR_ADDR(PIN##portid), \
  119. .input_ddr = _SFR_ADDR(DDR##portid), \
  120. .input_bit = bit, \
  121. .flags = _flags \
  122. }
  123. #define DEF_OUTPUT(portid, bit, _flags) \
  124. struct output_pin output_pin_##portid##bit = { \
  125. .output_port = _SFR_ADDR(PORT##portid), \
  126. .output_ddr = _SFR_ADDR(DDR##portid), \
  127. .output_bit = bit, \
  128. .flags = _flags, \
  129. }
  130. #define NONE 0
  131. #if TARGET==0
  132. # include "target_cncjoints.c"
  133. #else
  134. # error "You must define a valid build target!"
  135. # error "Example: make TARGET=0"
  136. # error "See make help for more information"
  137. #endif
  138. /* Override dwell times in debugging mode. */
  139. #if DEBUG
  140. # undef DEBOUNCE_DWELL_TIME
  141. # define DEBOUNCE_DWELL_TIME MSEC_TO_USEC(4000)
  142. # undef DEBOUNCE_ACTIVE_TIME
  143. # define DEBOUNCE_ACTIVE_TIME MSEC_TO_USEC(2000)
  144. #endif
  145. #define MMIO8(mem_addr) _MMIO_BYTE(mem_addr)
  146. #define U32(value) ((uint32_t)(value))
  147. #define U64(value) ((uint64_t)(value))
  148. /* System timer calibration. */
  149. #if CPU_HZ == MHz(20)
  150. # define SYSTIMER_TIMERFREQ (1 << CS11) /* == CPU_HZ/8 */
  151. # define JIFFIES_PER_SECOND U64(2500000)
  152. #elif CPU_HZ == MHz(16)
  153. # define SYSTIMER_TIMERFREQ (1 << CS11) /* == CPU_HZ/8 */
  154. # define JIFFIES_PER_SECOND U64(2000000)
  155. #else
  156. # error "No timer calibration for the selected CPU frequency available."
  157. #endif
  158. /* Convert values to jiffies. (Expensive on non-const values!) */
  159. #define MSEC_TO_JIFFIES(msec) U32(U64(msec) * JIFFIES_PER_SECOND / U64(1000))
  160. #define USEC_TO_JIFFIES(usec) U32(U64(usec) * JIFFIES_PER_SECOND / U64(1000000))
  161. /* Convert time values. (Expensive on non-const values!) */
  162. #define USEC_TO_MSEC(usec) U64(U64(usec) / U64(1000))
  163. #define MSEC_TO_USEC(msec) U64(U64(msec) * U64(1000))
  164. /* Jiffies timing helpers derived from the Linux Kernel sources.
  165. * These inlines deal with timer wrapping correctly.
  166. *
  167. * time_after(a, b) returns true if the time a is after time b.
  168. *
  169. * Do this with "<0" and ">=0" to only test the sign of the result. A
  170. * good compiler would generate better code (and a really good compiler
  171. * wouldn't care). Gcc is currently neither.
  172. */
  173. #define time_after(a, b) ((int32_t)(b) - (int32_t)(a) < 0)
  174. #define time_before(a, b) time_after(b, a)
  175. /* Upper 16-bit half of the jiffies counter.
  176. * The lower half is the hardware timer counter. */
  177. static uint16_t jiffies_high16;
  178. /* Timer 1 overflow IRQ handler.
  179. * This handler is executed on overflow of the (low) hardware part of
  180. * the jiffies counter. It does only add 0x10000 to the 32bit software
  181. * counter. So it basically adds 1 to the high 16bit software part of
  182. * the counter. */
  183. #define JIFFY_ISR_NAME stringify(TIMER1_OVF_vect)
  184. __asm__(
  185. ".text \n"
  186. ".global " JIFFY_ISR_NAME " \n"
  187. JIFFY_ISR_NAME ": \n"
  188. " push r0 \n"
  189. " in r0, __SREG__ \n"
  190. " push r16 \n"
  191. " lds r16, jiffies_high16 + 0 \n"
  192. " subi r16, lo8(-1) \n"
  193. " sts jiffies_high16 + 0, r16 \n"
  194. " lds r16, jiffies_high16 + 1 \n"
  195. " sbci r16, hi8(-1) \n"
  196. " sts jiffies_high16 + 1, r16 \n"
  197. " pop r16 \n"
  198. " out __SREG__, r0 \n"
  199. " pop r0 \n"
  200. " reti \n"
  201. ".previous \n"
  202. );
  203. static uint32_t get_jiffies(void)
  204. {
  205. uint16_t low;
  206. uint16_t high;
  207. /* We protect against (unlikely) overflow-while-read. */
  208. irq_disable();
  209. while (1) {
  210. if (unlikely(TIFR1 & (1 << TOV1))) {
  211. jiffies_high16++;
  212. TIFR1 |= (1 << TOV1); /* Clear it */
  213. }
  214. mb();
  215. low = TCNT1;
  216. high = jiffies_high16;
  217. mb();
  218. if (likely(!(TIFR1 & (1 << TOV1))))
  219. break; /* No overflow */
  220. }
  221. irq_enable();
  222. /* This 16bit shift basically is for free. */
  223. return ((((uint32_t)high) << 16) | low);
  224. }
  225. /* Put a 5ms signal onto the test pin. */
  226. static void jiffies_test(void)
  227. {
  228. uint32_t now, next;
  229. return; /* Disabled */
  230. irq_enable();
  231. now = get_jiffies();
  232. next = now + MSEC_TO_JIFFIES(5);
  233. while (1) {
  234. wdt_reset();
  235. now = get_jiffies();
  236. if (time_after(now, next)) {
  237. TEST_PORT ^= (1 << TEST_BIT);
  238. next = now + MSEC_TO_JIFFIES(5);
  239. }
  240. }
  241. }
  242. static void setup_jiffies(void)
  243. {
  244. /* Initialize the system timer */
  245. TCCR1A = 0;
  246. TCCR1B = SYSTIMER_TIMERFREQ; /* Speed */
  247. TIMSK1 |= (1 << TOIE1); /* Overflow IRQ */
  248. jiffies_test();
  249. }
  250. /* We can keep this in SRAM. It's not that big. */
  251. static const uint8_t bit2mask_lt[] = {
  252. 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
  253. };
  254. /* Convert a bit-number to a bit-mask.
  255. * Only valid for bitnr<=7.
  256. */
  257. #define BITMASK(bitnr) (__builtin_constant_p(bitnr) ? (1 << (bitnr)) : bit2mask_lt[(bitnr)])
  258. /* Set the hardware state of an output pin. */
  259. static inline void output_hw_set(struct output_pin *out, bool state)
  260. {
  261. if (out->flags & OUTPUT_INVERT)
  262. state = !state;
  263. if (state)
  264. MMIO8(out->output_port) |= BITMASK(out->output_bit);
  265. else
  266. MMIO8(out->output_port) &= ~BITMASK(out->output_bit);
  267. }
  268. /* Increment the trigger level of an output. */
  269. static inline void output_level_inc(struct output_pin *out)
  270. {
  271. if (out->level == 0)
  272. output_hw_set(out, 1);
  273. out->level++;
  274. }
  275. /* Decrement the trigger level of an output. */
  276. static inline void output_level_dec(struct output_pin *out)
  277. {
  278. out->level--;
  279. if (out->level == 0)
  280. output_hw_set(out, 0);
  281. }
  282. static void setup_ports(void)
  283. {
  284. struct connection *conn;
  285. uint8_t i;
  286. uint32_t now = get_jiffies();
  287. for (i = 0; i < ARRAY_SIZE(connections); i++) {
  288. conn = &(connections[i]);
  289. /* Init DDR registers */
  290. MMIO8(conn->in.input_ddr) &= ~BITMASK(conn->in.input_bit);
  291. MMIO8(conn->out->output_ddr) |= BITMASK(conn->out->output_bit);
  292. /* Enable/Disable pullup */
  293. if (conn->in.flags & INPUT_PULLUP)
  294. MMIO8(conn->in.input_port) |= BITMASK(conn->in.input_bit);
  295. else
  296. MMIO8(conn->in.input_port) &= ~BITMASK(conn->in.input_bit);
  297. /* Disable output signal */
  298. conn->out->level = 0;
  299. output_hw_set(conn->out, 0);
  300. conn->input_is_asserted = 0;
  301. conn->dwell_timeout = now + USEC_TO_JIFFIES(DEBOUNCE_ACTIVE_TIME);
  302. }
  303. }
  304. static void scan_one_input_pin(struct connection *conn, uint32_t now)
  305. {
  306. uint8_t hw_input_asserted;
  307. /* Get the input state */
  308. hw_input_asserted = (MMIO8(conn->in.input_pin) & BITMASK(conn->in.input_bit));
  309. /* The hw input state meaning changes, if PULLUP xor INVERT is used.*/
  310. if (!!(conn->in.flags & INPUT_PULLUP) ^ !!(conn->in.flags & INPUT_INVERT))
  311. hw_input_asserted = !hw_input_asserted;
  312. if (conn->input_is_asserted) {
  313. /* Signal currently is asserted in software.
  314. * Try to detect !hw_input_asserted, but honor the dwell time. */
  315. if (hw_input_asserted) {
  316. /* The hardware pin is still active.
  317. * Restart the dwell time. */
  318. conn->dwell_timeout = now + USEC_TO_JIFFIES(DEBOUNCE_DWELL_TIME);
  319. }
  320. if (hw_input_asserted || time_before(now, conn->dwell_timeout)) {
  321. /* wait... */
  322. return;
  323. }
  324. conn->input_is_asserted = 0;
  325. output_level_dec(conn->out);
  326. conn->dwell_timeout = now + USEC_TO_JIFFIES(DEBOUNCE_ACTIVE_TIME);
  327. } else {
  328. /* Signal currently is _not_ asserted in software.
  329. * Try to detect hw_input_asserted, but honor the dwell time. */
  330. if (!hw_input_asserted) {
  331. /* The hardware pin still isn't active.
  332. * Restart the dwell time. */
  333. conn->dwell_timeout = now + USEC_TO_JIFFIES(DEBOUNCE_ACTIVE_TIME);
  334. }
  335. if (!hw_input_asserted || time_before(now, conn->dwell_timeout)) {
  336. /* wait... */
  337. return;
  338. }
  339. conn->input_is_asserted = 1;
  340. output_level_inc(conn->out);
  341. conn->dwell_timeout = now + USEC_TO_JIFFIES(DEBOUNCE_DWELL_TIME);
  342. }
  343. }
  344. static void scan_input_pins(void)
  345. {
  346. uint8_t i;
  347. uint32_t now;
  348. while (1) {
  349. now = get_jiffies();
  350. for (i = 0; i < ARRAY_SIZE(connections); i++) {
  351. scan_one_input_pin(&(connections[i]), now);
  352. wdt_reset();
  353. }
  354. #if 0
  355. TEST_PORT ^= (1 << TEST_BIT);
  356. #endif
  357. }
  358. }
  359. static void major_fault(void)
  360. {
  361. emergency_shutdown();
  362. /* Pull test port high for failure indication. */
  363. TEST_DDR |= (1 << TEST_BIT);
  364. TEST_PORT |= (1 << TEST_BIT);
  365. while (1);
  366. }
  367. int main(void)
  368. {
  369. irq_disable();
  370. TEST_DDR |= (1 << TEST_BIT);
  371. TEST_PORT &= ~(1 << TEST_BIT);
  372. setup_jiffies();
  373. setup_ports();
  374. #if 0
  375. /* Check if we had a major fault. */
  376. if (!(MCUSR & (1 << PORF))) {
  377. if (MCUSR & (1 << WDRF))
  378. major_fault(); /* Watchdog triggered */
  379. }
  380. MCUSR = 0;
  381. #if !DEBUG
  382. wdt_enable(WDTO_500MS);
  383. #endif
  384. wdt_reset();
  385. #endif
  386. irq_enable();
  387. scan_input_pins();
  388. }