pcint.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*
  2. * Pin change interrupt handling
  3. *
  4. * Copyright (c) 2020 Michael Buesch <m@bues.ch>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "compat.h"
  21. #include "util.h"
  22. #include "pcint.h"
  23. #define NR_PCINT 24u
  24. static struct {
  25. pcint_callback_t cb[NR_PCINT];
  26. } pcint;
  27. #if USE_PCINT
  28. static uint8_t pcint_to_regnr(uint8_t index)
  29. {
  30. if (index <= 7u)
  31. return 0u;
  32. if (8u <= index && index <= 14u)
  33. return 1u;
  34. if (16u <= index && index <= 23u)
  35. return 2u;
  36. return 0u; /* invalid */
  37. }
  38. #endif /* USE_PCINT */
  39. #if USE_PCINT
  40. static uint8_t pcint_to_bitnr(uint8_t index)
  41. {
  42. if (index <= 7u)
  43. return index;
  44. if (8u <= index && index <= 14u)
  45. return (uint8_t)(index - 8u);
  46. if (16u <= index && index <= 23u)
  47. return (uint8_t)(index - 16u);
  48. return 0u; /* invalid */
  49. }
  50. #endif /* USE_PCINT */
  51. #define CASE_PCMSK(_regnr, _index, _set) \
  52. case _regnr: \
  53. if (_set) \
  54. PCMSK##_regnr = (uint8_t)(PCMSK##_regnr | (1u << pcint_to_bitnr(_index))); \
  55. else \
  56. PCMSK##_regnr = (uint8_t)(PCMSK##_regnr & ~(1u << pcint_to_bitnr(_index))); \
  57. break;
  58. static void pcint_reg_msk(uint8_t index, bool set)
  59. {
  60. #if USE_PCINT
  61. switch (pcint_to_regnr(index)) {
  62. default:
  63. CASE_PCMSK(0, index, set);
  64. CASE_PCMSK(1, index, set);
  65. CASE_PCMSK(2, index, set);
  66. }
  67. #endif /* USE_PCINT */
  68. }
  69. #define PCINT_ISR(_regnr) \
  70. ISR(PCINT##_regnr##_vect) \
  71. { \
  72. uint8_t i; \
  73. for (i = 0u; i < NR_PCINT; i++) { \
  74. if (pcint_to_regnr(i) == _regnr) { \
  75. if (pcint.cb[i]) \
  76. pcint.cb[i](); \
  77. } \
  78. } \
  79. }
  80. #if USE_PCINT
  81. PCINT_ISR(0)
  82. PCINT_ISR(1)
  83. PCINT_ISR(2)
  84. #endif /* USE_PCINT */
  85. #define CASE_CLRIF(_regnr) \
  86. case _regnr: \
  87. PCIFR = (1u << PCIF##_regnr); \
  88. break;
  89. void pcint_clear_irq(uint8_t index)
  90. {
  91. #if USE_PCINT
  92. switch (pcint_to_regnr(index)) {
  93. default:
  94. CASE_CLRIF(0);
  95. CASE_CLRIF(1);
  96. CASE_CLRIF(2);
  97. }
  98. #endif /* USE_PCINT */
  99. }
  100. #define UPDATE_PCICR(_regnr) \
  101. do { \
  102. if (PCMSK##_regnr == 0u) { \
  103. PCICR &= (uint8_t)~(1u << PCIE##_regnr); \
  104. } else { \
  105. if (!(PCICR & (1u << PCIE##_regnr))) { \
  106. PCIFR = (1u << PCIF##_regnr); \
  107. PCICR |= (1u << PCIE##_regnr); \
  108. } \
  109. } \
  110. } while (0)
  111. void pcint_enable(uint8_t index, bool enable)
  112. {
  113. pcint_reg_msk(index, enable);
  114. IF_PCINT(
  115. UPDATE_PCICR(0);
  116. UPDATE_PCICR(1);
  117. UPDATE_PCICR(2);
  118. )
  119. }
  120. void pcint_register_callback(uint8_t index, pcint_callback_t cb)
  121. {
  122. if (USE_PCINT) {
  123. if (index < NR_PCINT)
  124. pcint.cb[index] = cb;
  125. }
  126. }