gblink.c 6.3 KB

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