memsic_2125_app.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. @CodeAllNight
  3. https://github.com/jamisonderek/flipper-zero-tutorials
  4. This is a demostration of using GPIO interrupts to interpret data from the
  5. Memsic 2125 (Mx2125) Dual-Axis Accelerometer.
  6. Memsic - Name - Purpose - Flipper
  7. ====== ==== ========================================== =============
  8. Pin 1 - Tout - Temperature Out - not connected
  9. Pin 2 - Yout - Y-axis PWM Out (100Hz, duty cycle = value) - C0
  10. Pin 3 - GND - Ground - GND
  11. Pin 4 - GND - Ground - GND
  12. Pin 5 - Xout - X-axis PWM Out (100Hz, duty cycle = value) - C1
  13. Pin 6 - Vdd - Drain voltage (3.3V to 5V DC) - 3v3
  14. */
  15. #include <furi.h>
  16. #include <furi_hal.h>
  17. #include <furi_hal_gpio.h>
  18. #include <furi_hal_resources.h>
  19. #include <gui/gui.h>
  20. #include <locale/locale.h>
  21. #include <notification/notification.h>
  22. #include <notification/notification_messages.h>
  23. #define TAG "memsic_2125_app"
  24. typedef enum {
  25. DemoEventTypeKey,
  26. } DemoEventType;
  27. typedef struct {
  28. DemoEventType type; // The reason for this event.
  29. InputEvent input; // This data is specific to keypress data.
  30. } DemoEvent;
  31. typedef struct {
  32. const GpioPin* pin; // pin being monitored.
  33. uint32_t high; // timestamp of when pin went high.
  34. uint32_t low; // timestamp of when pin when low.
  35. float value; // duty cycle (0.0 to 100.0)
  36. bool reading; // when true, then value will not be updated.
  37. } AxisData;
  38. typedef struct {
  39. FuriString* buffer;
  40. AxisData* xData;
  41. AxisData* yData;
  42. } DemoData;
  43. typedef struct {
  44. FuriMessageQueue* queue; // Message queue
  45. DemoData* data;
  46. } DemoContext;
  47. // Invoked when input (button press) is detected.
  48. // We queue a message and then return to the caller.
  49. // @input_event the button that triggered the callback.
  50. // @queue our message queue.
  51. static void input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
  52. furi_assert(queue);
  53. DemoEvent event = {.type = DemoEventTypeKey, .input = *input_event};
  54. furi_message_queue_put(queue, &event, FuriWaitForever);
  55. }
  56. // Invoked whenever the monitored pin changes state.
  57. // @data pointer to an AxisData.
  58. void pulse_callback(void* data) {
  59. // Get the current time from high-resolution timer.
  60. FuriHalCortexTimer timer = furi_hal_cortex_timer_get(0);
  61. uint32_t now = timer.start;
  62. // Our parameter is a pointer to the axis data.
  63. furi_assert(data);
  64. AxisData* d = (AxisData*)data;
  65. // Get the current state of the pin (true = 3.3volts, false = GND)
  66. bool state = furi_hal_gpio_read(d->pin);
  67. if(state) {
  68. // state=true, so we are in GND->3v3 transition.
  69. // See if we have timings for both the high & low transitions.
  70. if((d->high != 0) && (d->low != 0) && (d->high < d->low) && !d->reading) {
  71. uint32_t durationCycle = now - d->high;
  72. uint32_t durationLow = d->low - d->high;
  73. // Update the value of the axis to reflect the duty cycle.
  74. d->value = (100.0f * durationLow) / durationCycle;
  75. }
  76. // Store the current time that the rise transition happened.
  77. d->high = timer.start;
  78. } else {
  79. // Store the current time that the fall transition happened.
  80. d->low = timer.start;
  81. }
  82. }
  83. // Invoked by the draw callback to render the screen.
  84. // We render our UI on the callback thread.
  85. // @canvas the surface to render our UI
  86. // @ctx a pointer to a DemoContext object.
  87. static void render_callback(Canvas* canvas, void* ctx) {
  88. // Attempt to aquire context, so we can read the data.
  89. DemoContext* demo_context = ctx;
  90. DemoData* data = demo_context->data;
  91. canvas_set_font(canvas, FontPrimary);
  92. canvas_draw_str_aligned(canvas, 1, 1, AlignLeft, AlignTop, "Memsic 2125 C1:X C0:Y");
  93. data->xData->reading = true;
  94. data->yData->reading = true;
  95. furi_string_printf(
  96. data->buffer, "%0.3f %0.3f", (double)data->xData->value, (double)data->yData->value);
  97. data->xData->reading = false;
  98. data->yData->reading = false;
  99. canvas_draw_str_aligned(
  100. canvas, 30, 55, AlignLeft, AlignTop, furi_string_get_cstr(data->buffer));
  101. // Draw a circle based on the xData & yData information.
  102. data->xData->reading = true;
  103. uint8_t x = (0.5f + ((50.0f - data->xData->value) / 12.0f)) * 128;
  104. data->xData->reading = false;
  105. x = MAX(0, MIN(x, 127));
  106. data->yData->reading = true;
  107. uint8_t y = (0.5f + (((float)data->yData->value - 50.0f) / 12.0f)) * 64;
  108. data->yData->reading = false;
  109. y = MAX(0, MIN(y, 63));
  110. canvas_draw_circle(canvas, x, y, 2);
  111. }
  112. // Program entry point
  113. int32_t memsic_2125_app(void* p) {
  114. UNUSED(p);
  115. // Configure our initial data.
  116. DemoContext* demo_context = malloc(sizeof(DemoContext));
  117. demo_context->data = malloc(sizeof(DemoData));
  118. demo_context->data->buffer = furi_string_alloc();
  119. AxisData* xData = malloc(sizeof(AxisData));
  120. xData->pin = &gpio_ext_pc1;
  121. xData->high = 0;
  122. xData->low = 0;
  123. xData->reading = false;
  124. xData->value = 0;
  125. demo_context->data->xData = xData;
  126. AxisData* yData = malloc(sizeof(AxisData));
  127. yData->pin = &gpio_ext_pc0;
  128. yData->high = 0;
  129. yData->low = 0;
  130. yData->reading = false;
  131. yData->value = 0;
  132. demo_context->data->yData = yData;
  133. // Queue for events (tick or input)
  134. demo_context->queue = furi_message_queue_alloc(8, sizeof(DemoEvent));
  135. // x-axis is a 100Hz pulse, with variable duty-cycle (which we need to measure).
  136. // Invoke the pulse_callback method passing the xData structure whenever the pin
  137. // transitions (Rise GND->3v3 or Fall 3v3->GND).
  138. furi_hal_gpio_init(xData->pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh);
  139. furi_hal_gpio_add_int_callback(xData->pin, pulse_callback, xData);
  140. // y-axis is a 100Hz pulse, with variable duty-cycle (which we need to measure).
  141. // Invoke the pulse_callback method passing the xData structure whenever the pin
  142. // transitions (Rise GND->3v3 or Fall 3v3->GND).
  143. furi_hal_gpio_init(yData->pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh);
  144. furi_hal_gpio_add_int_callback(yData->pin, pulse_callback, yData);
  145. // Set ViewPort callbacks
  146. ViewPort* view_port = view_port_alloc();
  147. view_port_draw_callback_set(view_port, render_callback, demo_context);
  148. view_port_input_callback_set(view_port, input_callback, demo_context->queue);
  149. // Open GUI and register view_port
  150. Gui* gui = furi_record_open(RECORD_GUI);
  151. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  152. // Main loop
  153. DemoEvent event;
  154. bool processing = true;
  155. do {
  156. if(furi_message_queue_get(demo_context->queue, &event, FuriWaitForever) == FuriStatusOk) {
  157. FURI_LOG_T(TAG, "Got event type: %d", event.type);
  158. switch(event.type) {
  159. case DemoEventTypeKey:
  160. // Short press of back button exits the program.
  161. if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
  162. FURI_LOG_I(TAG, "Short-Back pressed. Exiting program.");
  163. processing = false;
  164. }
  165. break;
  166. default:
  167. break;
  168. }
  169. // Send signal to update the screen (callback will get invoked at some point later.)
  170. view_port_update(view_port);
  171. } else {
  172. // We had an issue getting message from the queue, so exit application.
  173. processing = false;
  174. }
  175. } while(processing);
  176. // Free resources
  177. furi_hal_gpio_remove_int_callback(yData->pin);
  178. furi_hal_gpio_remove_int_callback(xData->pin);
  179. view_port_enabled_set(view_port, false);
  180. gui_remove_view_port(gui, view_port);
  181. view_port_free(view_port);
  182. furi_record_close(RECORD_GUI);
  183. furi_message_queue_free(demo_context->queue);
  184. furi_string_free(demo_context->data->buffer);
  185. free(demo_context->data);
  186. free(demo_context);
  187. return 0;
  188. }