logic_analyzer_app.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #include "logic_analyzer_app.h"
  2. #include "logic_analyzer_icons.h"
  3. #define COUNT(x) ((size_t)(sizeof(x) / sizeof((x)[0])))
  4. static void render_callback(Canvas* const canvas, void* cb_ctx);
  5. static const GpioPin* gpios[] = {
  6. &gpio_ext_pc0,
  7. &gpio_ext_pc1,
  8. &gpio_ext_pc3,
  9. &gpio_ext_pb2,
  10. &gpio_ext_pb3,
  11. &gpio_ext_pa4,
  12. &gpio_ext_pa6,
  13. &gpio_ext_pa7};
  14. // static const char* gpio_names[] = {"PC0", "PC1", "PC3", "PB2", "PB3", "PA4", "PA6", "PA7"};
  15. static void render_callback(Canvas* const canvas, void* cb_ctx) {
  16. AppFSM* app = cb_ctx;
  17. furi_mutex_acquire(app->mutex, FuriWaitForever);
  18. if(app == NULL) {
  19. return;
  20. }
  21. if(!app->processing) {
  22. furi_mutex_release(app->mutex);
  23. return;
  24. }
  25. char buffer[64];
  26. int y = 10;
  27. canvas_draw_frame(canvas, 0, 0, 128, 64);
  28. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, "State");
  29. y += 10;
  30. if(app->uart) {
  31. UsbUartState st;
  32. usb_uart_get_state(app->uart, &st);
  33. snprintf(buffer, sizeof(buffer), "Rx %ld / Tx %ld", st.rx_cnt, st.tx_cnt);
  34. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  35. y += 20;
  36. }
  37. canvas_set_font(canvas, FontSecondary);
  38. if(app->sump) {
  39. snprintf(
  40. buffer,
  41. sizeof(buffer),
  42. "%02X %lX %ld %lX %lX %X",
  43. app->sump->flags,
  44. app->sump->divider,
  45. app->sump->delay_count,
  46. app->sump->trig_mask,
  47. app->sump->trig_values,
  48. app->sump->trig_config);
  49. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  50. y += 10;
  51. if(app->sump->armed) {
  52. snprintf(
  53. buffer,
  54. sizeof(buffer),
  55. "Captured: %u / %ld",
  56. app->capture_pos,
  57. app->sump->read_count);
  58. canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
  59. y += 20;
  60. }
  61. if(app->sump->armed) {
  62. elements_button_center(canvas, "Trigger");
  63. }
  64. }
  65. furi_mutex_release(app->mutex);
  66. }
  67. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  68. furi_assert(event_queue);
  69. /* better skip than sorry */
  70. if(furi_message_queue_get_count(event_queue) < QUEUE_SIZE) {
  71. AppEvent event = {.type = EventKeyPress, .input = *input_event};
  72. furi_message_queue_put(event_queue, &event, 100);
  73. }
  74. }
  75. static bool message_process(AppFSM* app) {
  76. bool processing = true;
  77. AppEvent event;
  78. FuriStatus event_status = furi_message_queue_get(app->event_queue, &event, 100);
  79. if(event_status != FuriStatusOk) {
  80. return true;
  81. }
  82. switch(event.type) {
  83. case EventKeyPress: {
  84. if(event.input.type != InputTypePress) {
  85. break;
  86. }
  87. switch(event.input.key) {
  88. case InputKeyUp:
  89. break;
  90. case InputKeyDown:
  91. break;
  92. case InputKeyRight:
  93. break;
  94. case InputKeyLeft:
  95. break;
  96. case InputKeyOk:
  97. /* when armed, trigger by pressing the button */
  98. if(app->sump->armed) {
  99. for(size_t pos = app->capture_pos; pos < app->sump->read_count; pos++) {
  100. app->capture_buffer[app->sump->read_count - 1 - pos] = 0;
  101. }
  102. app->sump->armed = false;
  103. AppEvent event = {.type = EventBufferFilled};
  104. furi_message_queue_put(app->event_queue, &event, 100);
  105. }
  106. break;
  107. case InputKeyBack:
  108. processing = false;
  109. break;
  110. default:
  111. break;
  112. }
  113. break;
  114. }
  115. case EventBufferFilled: {
  116. usb_uart_tx_data(app->uart, app->capture_buffer, app->sump->read_count);
  117. break;
  118. }
  119. default: {
  120. break;
  121. }
  122. }
  123. return processing;
  124. }
  125. size_t data_received(void* ctx, uint8_t* data, size_t length) {
  126. AppFSM* app = (AppFSM*)ctx;
  127. snprintf(
  128. app->state_string,
  129. sizeof(app->state_string),
  130. "Rx: %02x '%c' (total %u)",
  131. data[0],
  132. data[0],
  133. length);
  134. return sump_handle(app->sump, data, length);
  135. }
  136. void tx_sump_tx(void* ctx, uint8_t* data, size_t length) {
  137. AppFSM* app = (AppFSM*)ctx;
  138. usb_uart_tx_data(app->uart, data, length);
  139. }
  140. static uint8_t levels_get(AppFSM* app) {
  141. UNUSED(app);
  142. uint32_t port_a = GPIOA->IDR;
  143. uint32_t port_b = GPIOB->IDR;
  144. uint32_t port_c = GPIOC->IDR;
  145. /* 7 6 5 4 3 2 1 0
  146. A7 A6 A4 B3 B2 C3 C1 C0 */
  147. uint8_t ret = (port_a & 0xC0) | ((port_a & 0x10) << 1) | ((port_b & 0x0C) << 1) |
  148. ((port_c & 0x08) >> 1) | (port_c & 0x03);
  149. return ret;
  150. }
  151. static int32_t capture_thread_worker(void* context) {
  152. AppFSM* app = (AppFSM*)context;
  153. uint8_t prev_levels = 0;
  154. while(app->processing) {
  155. app->current_levels = levels_get(app);
  156. if(app->sump->armed) {
  157. uint8_t relevant_levels = app->current_levels & app->sump->trig_mask;
  158. uint8_t prev_relevant_levels = prev_levels & app->sump->trig_mask;
  159. if(relevant_levels != prev_relevant_levels) {
  160. prev_levels = app->current_levels;
  161. app->capture_buffer[app->sump->read_count - 1 - app->capture_pos++] =
  162. app->current_levels;
  163. if(app->capture_pos >= app->sump->read_count) {
  164. app->sump->armed = false;
  165. AppEvent event = {.type = EventBufferFilled};
  166. furi_message_queue_put(app->event_queue, &event, 100);
  167. }
  168. }
  169. } else {
  170. prev_levels = app->current_levels;
  171. app->capture_pos = 0;
  172. app->triggered = false;
  173. prev_levels = 0;
  174. furi_delay_ms(50);
  175. }
  176. }
  177. return 0;
  178. }
  179. static bool app_init(AppFSM* const app) {
  180. strcpy(app->state_string, "none");
  181. app->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  182. if(!app->mutex) {
  183. FURI_LOG_E(TAG, "cannot create mutex\r\n");
  184. free(app);
  185. return false;
  186. }
  187. app->processing = true;
  188. app->notification = furi_record_open(RECORD_NOTIFICATION);
  189. app->gui = furi_record_open(RECORD_GUI);
  190. app->dialogs = furi_record_open(RECORD_DIALOGS);
  191. app->storage = furi_record_open(RECORD_STORAGE);
  192. app->view_port = view_port_alloc();
  193. app->event_queue = furi_message_queue_alloc(QUEUE_SIZE, sizeof(AppEvent));
  194. view_port_draw_callback_set(app->view_port, render_callback, app);
  195. view_port_input_callback_set(app->view_port, input_callback, app->event_queue);
  196. gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
  197. UsbUartConfig uart_config;
  198. uart_config.vcp_ch = 1;
  199. uart_config.rx_data = &data_received;
  200. uart_config.rx_data_ctx = app;
  201. app->uart = usb_uart_enable(&uart_config);
  202. app->sump = sump_alloc();
  203. app->sump->tx_data = tx_sump_tx;
  204. app->sump->tx_data_ctx = app;
  205. app->capture_buffer = malloc(MAX_SAMPLE_MEM);
  206. for(size_t io = 0; io < COUNT(gpios); io++) {
  207. furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullNo, GpioSpeedVeryHigh);
  208. }
  209. app->capture_thread = furi_thread_alloc_ex("capture_thread", 1024, capture_thread_worker, app);
  210. furi_thread_start(app->capture_thread);
  211. return true;
  212. }
  213. static void app_deinit(AppFSM* const app) {
  214. view_port_enabled_set(app->view_port, false);
  215. gui_remove_view_port(app->gui, app->view_port);
  216. view_port_free(app->view_port);
  217. furi_message_queue_free(app->event_queue);
  218. furi_mutex_free(app->mutex);
  219. furi_thread_join(app->capture_thread);
  220. furi_thread_free(app->capture_thread);
  221. free(app->capture_buffer);
  222. sump_free(app->sump);
  223. usb_uart_disable(app->uart);
  224. furi_record_close(RECORD_STORAGE);
  225. furi_record_close(RECORD_DIALOGS);
  226. furi_record_close(RECORD_GUI);
  227. furi_record_close(RECORD_NOTIFICATION);
  228. }
  229. int32_t logic_analyzer_app_main(void* p) {
  230. UNUSED(p);
  231. AppFSM* app = malloc(sizeof(AppFSM));
  232. app_init(app);
  233. dolphin_deed(DolphinDeedPluginGameStart);
  234. notification_message_block(app->notification, &sequence_display_backlight_enforce_on);
  235. while(app->processing) {
  236. app->processing = message_process(app);
  237. view_port_update(app->view_port);
  238. }
  239. notification_message_block(app->notification, &sequence_display_backlight_enforce_auto);
  240. app_deinit(app);
  241. free(app);
  242. return 0;
  243. }