GPIO.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * This file is free software: you can redistribute it and/or modify it
  3. * under the terms of the GNU General Public License as published by the
  4. * Free Software Foundation, either version 3 of the License, or
  5. * (at your option) any later version.
  6. *
  7. * This file is distributed in the hope that it will be useful, but
  8. * WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. * See the GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License along
  13. * with this program. If not, see <http://www.gnu.org/licenses/>.
  14. *
  15. * Code by Andrew Tridgell and Siddharth Bharat Purohit
  16. */
  17. #include "GPIO.h"
  18. #include <AP_BoardConfig/AP_BoardConfig.h>
  19. using namespace ChibiOS;
  20. // GPIO pin table from hwdef.dat
  21. static struct gpio_entry {
  22. uint8_t pin_num;
  23. bool enabled;
  24. uint8_t pwm_num;
  25. ioline_t pal_line;
  26. AP_HAL::GPIO::irq_handler_fn_t fn; // callback for GPIO interface
  27. bool is_input;
  28. uint8_t mode;
  29. } _gpio_tab[] = HAL_GPIO_PINS;
  30. #define NUM_PINS ARRAY_SIZE(_gpio_tab)
  31. #define PIN_ENABLED(pin) ((pin)<NUM_PINS && _gpio_tab[pin].enabled)
  32. /*
  33. map a user pin number to a GPIO table entry
  34. */
  35. static struct gpio_entry *gpio_by_pin_num(uint8_t pin_num, bool check_enabled=true)
  36. {
  37. for (uint8_t i=0; i<ARRAY_SIZE(_gpio_tab); i++) {
  38. if (pin_num == _gpio_tab[i].pin_num) {
  39. if (check_enabled && !_gpio_tab[i].enabled) {
  40. return NULL;
  41. }
  42. return &_gpio_tab[i];
  43. }
  44. }
  45. return NULL;
  46. }
  47. static void pal_interrupt_cb(void *arg);
  48. static void pal_interrupt_cb_functor(void *arg);
  49. GPIO::GPIO()
  50. {}
  51. void GPIO::init()
  52. {
  53. // auto-disable pins being used for PWM output based on BRD_PWM_COUNT parameter
  54. uint8_t pwm_count = AP_BoardConfig::get_pwm_count();
  55. for (uint8_t i=0; i<ARRAY_SIZE(_gpio_tab); i++) {
  56. struct gpio_entry *g = &_gpio_tab[i];
  57. if (g->pwm_num != 0) {
  58. g->enabled = g->pwm_num > pwm_count;
  59. }
  60. }
  61. }
  62. void GPIO::pinMode(uint8_t pin, uint8_t output)
  63. {
  64. struct gpio_entry *g = gpio_by_pin_num(pin);
  65. if (g) {
  66. if (!output && g->is_input &&
  67. (g->mode == PAL_MODE_INPUT_PULLUP ||
  68. g->mode == PAL_MODE_INPUT_PULLDOWN)) {
  69. // already set
  70. return;
  71. }
  72. g->mode = output?PAL_MODE_OUTPUT_PUSHPULL:PAL_MODE_INPUT;
  73. palSetLineMode(g->pal_line, g->mode);
  74. g->is_input = !output;
  75. }
  76. }
  77. uint8_t GPIO::read(uint8_t pin)
  78. {
  79. struct gpio_entry *g = gpio_by_pin_num(pin);
  80. if (g) {
  81. return palReadLine(g->pal_line);
  82. }
  83. return 0;
  84. }
  85. void GPIO::write(uint8_t pin, uint8_t value)
  86. {
  87. struct gpio_entry *g = gpio_by_pin_num(pin);
  88. if (g) {
  89. if (g->is_input) {
  90. // control pullup/pulldown
  91. g->mode = value==1?PAL_MODE_INPUT_PULLUP:PAL_MODE_INPUT_PULLDOWN;
  92. palSetLineMode(g->pal_line, g->mode);
  93. } else if (value == PAL_LOW) {
  94. palClearLine(g->pal_line);
  95. } else {
  96. palSetLine(g->pal_line);
  97. }
  98. }
  99. }
  100. void GPIO::toggle(uint8_t pin)
  101. {
  102. struct gpio_entry *g = gpio_by_pin_num(pin);
  103. if (g) {
  104. palToggleLine(g->pal_line);
  105. }
  106. }
  107. /* Alternative interface: */
  108. AP_HAL::DigitalSource* GPIO::channel(uint16_t pin)
  109. {
  110. struct gpio_entry *g = gpio_by_pin_num(pin);
  111. if (!g) {
  112. return nullptr;
  113. }
  114. return new DigitalSource(g->pal_line);
  115. }
  116. extern const AP_HAL::HAL& hal;
  117. /*
  118. Attach an interrupt handler to a GPIO pin number. The pin number
  119. must be one specified with a GPIO() marker in hwdef.dat
  120. */
  121. bool GPIO::attach_interrupt(uint8_t pin,
  122. irq_handler_fn_t fn,
  123. INTERRUPT_TRIGGER_TYPE mode)
  124. {
  125. struct gpio_entry *g = gpio_by_pin_num(pin, false);
  126. if (!g) {
  127. return false;
  128. }
  129. if (!_attach_interrupt(g->pal_line,
  130. palcallback_t(fn?pal_interrupt_cb_functor:nullptr),
  131. g,
  132. mode)) {
  133. return false;
  134. }
  135. g->fn = fn;
  136. return true;
  137. }
  138. /*
  139. Attach an interrupt handler to ioline_t
  140. */
  141. bool GPIO::_attach_interrupt(ioline_t line, AP_HAL::Proc p, uint8_t mode)
  142. {
  143. return _attach_interrupt(line, palcallback_t(p?pal_interrupt_cb:nullptr), (void*)p, mode);
  144. }
  145. bool GPIO::attach_interrupt(uint8_t pin,
  146. AP_HAL::Proc proc,
  147. INTERRUPT_TRIGGER_TYPE mode) {
  148. struct gpio_entry *g = gpio_by_pin_num(pin, false);
  149. if (!g) {
  150. return false;
  151. }
  152. return _attach_interrupt(g->pal_line, proc, mode);
  153. }
  154. bool GPIO::_attach_interrupt(ioline_t line, palcallback_t cb, void *p, uint8_t mode)
  155. {
  156. uint32_t chmode = 0;
  157. switch(mode) {
  158. case INTERRUPT_FALLING:
  159. chmode = PAL_EVENT_MODE_FALLING_EDGE;
  160. break;
  161. case INTERRUPT_RISING:
  162. chmode = PAL_EVENT_MODE_RISING_EDGE;
  163. break;
  164. case INTERRUPT_BOTH:
  165. chmode = PAL_EVENT_MODE_BOTH_EDGES;
  166. break;
  167. default:
  168. if (p) {
  169. return false;
  170. }
  171. break;
  172. }
  173. osalSysLock();
  174. palevent_t *pep = pal_lld_get_line_event(line);
  175. if (pep->cb && p != nullptr) {
  176. // the pad is already being used for a callback
  177. osalSysUnlock();
  178. return false;
  179. }
  180. if (!p) {
  181. chmode = PAL_EVENT_MODE_DISABLED;
  182. }
  183. palDisableLineEventI(line);
  184. palSetLineCallbackI(line, cb, p);
  185. palEnableLineEventI(line, chmode);
  186. osalSysUnlock();
  187. return true;
  188. }
  189. bool GPIO::usb_connected(void)
  190. {
  191. return _usb_connected;
  192. }
  193. DigitalSource::DigitalSource(ioline_t _line) :
  194. line(_line)
  195. {}
  196. void DigitalSource::mode(uint8_t output)
  197. {
  198. palSetLineMode(line, output);
  199. }
  200. uint8_t DigitalSource::read()
  201. {
  202. return palReadLine(line);
  203. }
  204. void DigitalSource::write(uint8_t value)
  205. {
  206. palWriteLine(line, value);
  207. }
  208. void DigitalSource::toggle()
  209. {
  210. palToggleLine(line);
  211. }
  212. void pal_interrupt_cb(void *arg)
  213. {
  214. if (arg != nullptr) {
  215. ((AP_HAL::Proc)arg)();
  216. }
  217. }
  218. void pal_interrupt_cb_functor(void *arg)
  219. {
  220. const uint32_t now = AP_HAL::micros();
  221. struct gpio_entry *g = (gpio_entry *)arg;
  222. if (g == nullptr) {
  223. // what?
  224. return;
  225. }
  226. if (!(g->fn)) {
  227. return;
  228. }
  229. (g->fn)(g->pin_num, palReadLine(g->pal_line), now);
  230. }