radio.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <furi_hal_gpio.h>
  4. #include <furi_hal_resources.h>
  5. #include <gui/gui.h>
  6. #include <locale/locale.h>
  7. #include <TEA5767.h>
  8. /// The band that will be tuned by this sketch is FM.
  9. #define FIX_BAND RADIO_BAND_FM
  10. /// The station that will be tuned by this sketch is 95.30 MHz.
  11. #define FIX_STATION 9530
  12. typedef enum {
  13. RadioStateNotFound,
  14. RadioStateFound,
  15. RadioStateWriteSuccess,
  16. RadioStateReadSuccess,
  17. RadioStateWriteReadSuccess,
  18. } RadioState;
  19. typedef enum {
  20. DemoEventTypeTick,
  21. DemoEventTypeKey,
  22. // You can add additional events here.
  23. } DemoEventType;
  24. typedef struct {
  25. DemoEventType type; // The reason for this event.
  26. InputEvent input; // This data is specific to keypress data.
  27. // You can add additional data that is helpful for your events.
  28. } DemoEvent;
  29. typedef struct {
  30. FuriString* buffer;
  31. // You can add additional state here.
  32. int address;
  33. RadioState state;
  34. int value;
  35. uint8_t registers[5];
  36. } DemoData;
  37. typedef struct {
  38. FuriMessageQueue* queue; // Message queue (DemoEvent items to process).
  39. FuriMutex* mutex; // Used to provide thread safe access to data.
  40. DemoData* data; // Data accessed by multiple threads (acquire the mutex before accessing!)
  41. } DemoContext;
  42. // Invoked when input (button press) is detected. We queue a message and then return to the caller.
  43. static void input_callback(InputEvent* input_event, FuriMessageQueue* queue) {
  44. furi_assert(queue);
  45. DemoEvent event = {.type = DemoEventTypeKey, .input = *input_event};
  46. furi_message_queue_put(queue, &event, FuriWaitForever);
  47. }
  48. // Invoked by the timer on every tick. We queue a message and then return to the caller.
  49. static void tick_callback(void* ctx) {
  50. furi_assert(ctx);
  51. FuriMessageQueue* queue = ctx;
  52. DemoEvent event = {.type = DemoEventTypeTick};
  53. // It's OK to loose this event if system overloaded (so we don't pass a wait value for 3rd parameter.)
  54. furi_message_queue_put(queue, &event, 0);
  55. }
  56. // Invoked by the draw callback to render the screen. We render our UI on the callback thread.
  57. static void render_callback(Canvas* canvas, void* ctx) {
  58. // Attempt to aquire context, so we can read the data.
  59. DemoContext* demo_context = ctx;
  60. if(furi_mutex_acquire(demo_context->mutex, 200) != FuriStatusOk) {
  61. return;
  62. }
  63. DemoData* data = demo_context->data;
  64. canvas_set_font(canvas, FontPrimary);
  65. if(data->address == TEA5767_ADR) {
  66. canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, "FOUND I2C DEVICE");
  67. furi_string_printf(data->buffer, "Address 0x%02x", (data->address));
  68. canvas_draw_str_aligned(
  69. canvas, 64, 30, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
  70. if(data->state == RadioStateFound) {
  71. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "FOUND DEVICE");
  72. } else if(data->state == RadioStateWriteSuccess) {
  73. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "WRITE SUCCESS");
  74. } else if(data->state == RadioStateReadSuccess) {
  75. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "READ SUCCESS");
  76. } else if(data->state == RadioStateWriteReadSuccess) {
  77. canvas_draw_str_aligned(
  78. canvas, 64, 40, AlignCenter, AlignCenter, "WRITE/READ SUCCESS");
  79. }
  80. // furi_string_printf(data->buffer, "value %d", (data->value));
  81. furi_string_printf(
  82. data->buffer,
  83. "registers: %02X%02X%02X%02X%02X",
  84. data->registers[0],
  85. data->registers[1],
  86. data->registers[2],
  87. data->registers[3],
  88. data->registers[4]);
  89. canvas_draw_str_aligned(
  90. canvas, 64, 50, AlignCenter, AlignCenter, furi_string_get_cstr(data->buffer));
  91. } else {
  92. canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignCenter, "I2C NOT FOUND");
  93. canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, "pin15=SDA. pin16=SCL");
  94. canvas_draw_str_aligned(canvas, 64, 40, AlignCenter, AlignCenter, "pin9=VCC. pin18=GND");
  95. }
  96. // Release the context, so other threads can update the data.
  97. furi_mutex_release(demo_context->mutex);
  98. }
  99. // Our main loop invokes this method after acquiring the mutex, so we can safely access the protected data.
  100. static void update_i2c_status(void* ctx) {
  101. DemoContext* demo_context = ctx;
  102. DemoData* data = demo_context->data;
  103. data->address = 0;
  104. data->state = RadioStateNotFound;
  105. if(tea5767_is_device_ready()) {
  106. data->address = TEA5767_ADR;
  107. data->state = RadioStateFound;
  108. if(tea5767_init(data->registers)) {
  109. data->state = RadioStateWriteSuccess;
  110. int frequency = FIX_STATION;
  111. if(tea5767_set_frequency(data->registers, frequency)) {
  112. data->state = RadioStateWriteSuccess;
  113. data->value = 0;
  114. if(tea5767_get_frequency(data->registers, &data->value)) {
  115. data->state = RadioStateReadSuccess;
  116. }
  117. }
  118. }
  119. }
  120. }
  121. int32_t radio_app(void* p) {
  122. UNUSED(p);
  123. // Configure our initial data.
  124. DemoContext* demo_context = malloc(sizeof(DemoContext));
  125. demo_context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  126. demo_context->data = malloc(sizeof(DemoData));
  127. demo_context->data->buffer = furi_string_alloc();
  128. demo_context->data->address = 0;
  129. demo_context->data->state = RadioStateNotFound;
  130. demo_context->data->value = 0;
  131. demo_context->data->registers[0] = 0x00;
  132. demo_context->data->registers[1] = 0x00;
  133. demo_context->data->registers[2] = 0xB0;
  134. demo_context->data->registers[3] = REG_4_XTAL | REG_4_SMUTE;
  135. demo_context->data->registers[4] = 0x00;
  136. // Queue for events (tick or input)
  137. demo_context->queue = furi_message_queue_alloc(8, sizeof(DemoEvent));
  138. // Set ViewPort callbacks
  139. ViewPort* view_port = view_port_alloc();
  140. view_port_draw_callback_set(view_port, render_callback, demo_context);
  141. view_port_input_callback_set(view_port, input_callback, demo_context->queue);
  142. // Open GUI and register view_port
  143. Gui* gui = furi_record_open(RECORD_GUI);
  144. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  145. // Update the screen fairly frequently (every 1000 milliseconds = 1 second.)
  146. FuriTimer* timer = furi_timer_alloc(tick_callback, FuriTimerTypePeriodic, demo_context->queue);
  147. furi_timer_start(timer, 1000);
  148. // Main loop
  149. DemoEvent event;
  150. bool processing = true;
  151. do {
  152. if(furi_message_queue_get(demo_context->queue, &event, FuriWaitForever) == FuriStatusOk) {
  153. switch(event.type) {
  154. case DemoEventTypeKey:
  155. // Short press of back button exits the program.
  156. if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) {
  157. processing = false;
  158. }
  159. break;
  160. case DemoEventTypeTick:
  161. // Every timer tick we update the i2c status.
  162. furi_mutex_acquire(demo_context->mutex, FuriWaitForever);
  163. update_i2c_status(demo_context);
  164. furi_mutex_release(demo_context->mutex);
  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_timer_free(timer);
  178. view_port_enabled_set(view_port, false);
  179. gui_remove_view_port(gui, view_port);
  180. view_port_free(view_port);
  181. furi_record_close(RECORD_GUI);
  182. furi_message_queue_free(demo_context->queue);
  183. furi_mutex_free(demo_context->mutex);
  184. furi_string_free(demo_context->data->buffer);
  185. free(demo_context->data);
  186. free(demo_context);
  187. return 0;
  188. }