gblink.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-License-Identifier: BSD-2-Clause
  2. // Copyright (c) 2023 KBEmbedded
  3. #include <furi.h>
  4. #include <furi_hal.h>
  5. #include <stdint.h>
  6. #include "gblink.h"
  7. const struct gblink_pins common_pinouts[PINOUT_COUNT] = {
  8. /* Original */
  9. {
  10. &gpio_ext_pc3,
  11. &gpio_ext_pb3,
  12. &gpio_ext_pb2,
  13. &gpio_ext_pa4,
  14. },
  15. /* MALVEKE EXT1 */
  16. {
  17. &gpio_ext_pa6,
  18. &gpio_ext_pa7,
  19. &gpio_ext_pb3,
  20. &gpio_ext_pa4,
  21. },
  22. };
  23. struct gblink {
  24. const GpioPin *serin;
  25. const GpioPin *serout;
  26. const GpioPin *clk;
  27. const GpioPin *sd;
  28. uint8_t in;
  29. uint8_t out;
  30. uint8_t out_buf;
  31. bool out_buf_valid;
  32. uint8_t shift;
  33. uint8_t nobyte;
  34. gblink_clk_source source;
  35. gblink_mode mode;
  36. gblink_speed speed;
  37. uint32_t time;
  38. uint32_t bitclk_timeout_us;
  39. /* Clocks idle between bytes is nominally 430 us long for burst data,
  40. * 15 ms for idle polling (e.g. waiting for menu selection), some oddball
  41. * 2 ms gaps that appears between one 0xFE byte from the Game Boy every trade;
  42. * clock period is nominally 122 us.
  43. * Therefore, if we haven't seen a clock in 500 us, reset our bit counter.
  44. * Note that, this should never actually be a concern, but it is an additional
  45. * safeguard against desyncing.
  46. */
  47. void (*callback)(void* cb_context, uint8_t in);
  48. void *cb_context;
  49. };
  50. static void gblink_shift_in(struct gblink *gblink)
  51. {
  52. const uint32_t time_ticks = furi_hal_cortex_instructions_per_microsecond() * gblink->bitclk_timeout_us;
  53. /* If we exceeded the bit clock timeout, reset all counters */
  54. if ((DWT->CYCCNT - gblink->time) > time_ticks) {
  55. gblink->in = 0;
  56. gblink->shift = 0;
  57. }
  58. gblink->time = DWT->CYCCNT;
  59. gblink->in <<= 1;
  60. gblink->in |= furi_hal_gpio_read(gblink->serin);
  61. gblink->shift++;
  62. /* If 8 bits transfered, reset shift counter, call registered
  63. * callback, re-set nobyte in output buffer.
  64. */
  65. if (gblink->shift == 8) {
  66. gblink->shift = 0;
  67. /* Set up next out byte before calling the callback.
  68. * This is in case the callback itself sets a new out
  69. * byte which it will in most cases. It is up to the
  70. * main application at this time to ensure that
  71. * gblink_transfer() isn't called multiple times before
  72. * a byte has a chance to be sent out.
  73. */
  74. if (gblink->out_buf_valid) {
  75. gblink->out = gblink->out_buf;
  76. gblink->out_buf_valid = false;
  77. } else {
  78. gblink->out = gblink->nobyte;
  79. }
  80. gblink->callback(gblink->cb_context, gblink->in);
  81. }
  82. }
  83. static void gblink_shift_out(struct gblink *gblink)
  84. {
  85. furi_hal_gpio_write(gblink->serout, !!(gblink->out & 0x80));
  86. gblink->out <<= 1;
  87. }
  88. static void gblink_clk_callback(void *context)
  89. {
  90. furi_assert(context);
  91. struct gblink *gblink = context;
  92. if (furi_hal_gpio_read(gblink->clk)) {
  93. /* Posedge Shift in data */
  94. gblink_shift_in(gblink);
  95. } else {
  96. /* Negedge shift out data */
  97. gblink_shift_out(gblink);
  98. }
  99. }
  100. void gblink_clk_source_set(void *handle, int source)
  101. {
  102. furi_assert(handle);
  103. struct gblink *gblink = handle;
  104. gblink->source = source;
  105. gblink->shift = 0;
  106. }
  107. void gblink_speed_set(void *handle, gblink_speed speed)
  108. {
  109. furi_assert(handle);
  110. struct gblink *gblink = handle;
  111. gblink->speed = speed;
  112. }
  113. /* default is set to 500 us */
  114. void gblink_timeout_set(void *handle, uint32_t us)
  115. {
  116. furi_assert(handle);
  117. struct gblink *gblink = handle;
  118. gblink->bitclk_timeout_us = us;
  119. }
  120. void gblink_transfer(void *handle, uint8_t val)
  121. {
  122. furi_assert(handle);
  123. struct gblink *gblink = handle;
  124. /* This checks the value of gblink->shift which can change in the ISR.
  125. * Because of that, disable interrupts when checking gblink->shift and
  126. * setting gblink->out_buf_valid
  127. * If shift is 0, we're between bytes and can safely set the out byte.
  128. * If shift is nonzero, a byte is currently being transmitted. Set the
  129. * out_buf and set out_buf_valid. When the ISR is finished writing the
  130. * next byte it will check out_buf_valid and copy in out_buf.
  131. *
  132. * Realistically, this should only ever be called from the transfer
  133. * complete callback. There are few situations outside of that which
  134. * would make sense.
  135. *
  136. * Note that, this currently has no checks for if there is data already
  137. * pending to be transmitted. Calling this back to back can cause data
  138. * loss!
  139. */
  140. FURI_CRITICAL_ENTER();
  141. if (gblink->shift == 0) {
  142. gblink->out = val;
  143. gblink->out_buf_valid = false;
  144. } else {
  145. gblink->out_buf = val;
  146. gblink->out_buf_valid = true;
  147. }
  148. FURI_CRITICAL_EXIT();
  149. }
  150. void gblink_nobyte_set(void *handle, uint8_t val)
  151. {
  152. struct gblink *gblink = handle;
  153. gblink->nobyte = val;
  154. }
  155. void gblink_int_enable(void *handle)
  156. {
  157. furi_assert(handle);
  158. struct gblink *gblink = handle;
  159. furi_hal_gpio_enable_int_callback(gblink->clk);
  160. }
  161. void gblink_int_disable(void *handle)
  162. {
  163. furi_assert(handle);
  164. struct gblink *gblink = handle;
  165. furi_hal_gpio_disable_int_callback(gblink->clk);
  166. }
  167. void *gblink_alloc(struct gblink_def *gblink_def)
  168. {
  169. struct gblink *gblink;
  170. /* Allocate and zero struct */
  171. gblink = malloc(sizeof(struct gblink));
  172. /* Set struct values from function args */
  173. gblink->serin = gblink_def->pins->serin;
  174. gblink->serout = gblink_def->pins->serout;
  175. gblink->clk = gblink_def->pins->clk;
  176. gblink->sd = gblink_def->pins->sd;
  177. gblink->source = gblink_def->source;
  178. gblink->speed = GBLINK_SPD_8192HZ;
  179. /* Set up timeout variables */
  180. gblink->bitclk_timeout_us = 500;
  181. gblink->time = DWT->CYCCNT;
  182. /* Set up secondary callback */
  183. gblink->callback = gblink_def->callback;
  184. gblink->cb_context = gblink_def->cb_context;
  185. /* Set up pins */
  186. /* Currently assumes external clock source only */
  187. /* XXX: This might actually be open-drain on real GB hardware */
  188. furi_hal_gpio_write(gblink->serout, false);
  189. furi_hal_gpio_init(gblink->serout, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  190. furi_hal_gpio_write(gblink->serin, false);
  191. furi_hal_gpio_init(gblink->serin, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
  192. furi_hal_gpio_init(gblink->clk, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedVeryHigh);
  193. /* Set up interrupt on clock */
  194. /* This may not be needed after NFC refactor */
  195. furi_hal_gpio_remove_int_callback(gblink->clk);
  196. furi_hal_gpio_add_int_callback(gblink->clk, gblink_clk_callback, gblink);
  197. return gblink;
  198. }
  199. void gblink_free(void *handle)
  200. {
  201. furi_assert(handle);
  202. struct gblink *gblink = handle;
  203. /* Remove interrupt, set IO to sane state */
  204. furi_hal_gpio_remove_int_callback(gblink->clk);
  205. furi_hal_gpio_init_simple(gblink->serin, GpioModeAnalog);
  206. furi_hal_gpio_init_simple(gblink->serout, GpioModeAnalog);
  207. furi_hal_gpio_init_simple(gblink->clk, GpioModeAnalog);
  208. free(gblink);
  209. }