display_task.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #if SK_DISPLAY
  2. #include "display_task.h"
  3. #include "semaphore_guard.h"
  4. #include "font/roboto_light_60.h"
  5. static const uint8_t LEDC_CHANNEL_LCD_BACKLIGHT = 0;
  6. DisplayTask::DisplayTask(const uint8_t task_core) : Task{"Display", 2048, 1, task_core} {
  7. knob_state_queue_ = xQueueCreate(1, sizeof(PB_SmartKnobState));
  8. assert(knob_state_queue_ != NULL);
  9. mutex_ = xSemaphoreCreateMutex();
  10. assert(mutex_ != NULL);
  11. }
  12. DisplayTask::~DisplayTask() {
  13. vQueueDelete(knob_state_queue_);
  14. vSemaphoreDelete(mutex_);
  15. }
  16. void DisplayTask::run() {
  17. tft_.begin();
  18. tft_.invertDisplay(1);
  19. tft_.setRotation(SK_DISPLAY_ROTATION);
  20. tft_.fillScreen(TFT_DARKGREEN);
  21. ledcSetup(LEDC_CHANNEL_LCD_BACKLIGHT, 5000, 16);
  22. ledcAttachPin(PIN_LCD_BACKLIGHT, LEDC_CHANNEL_LCD_BACKLIGHT);
  23. ledcWrite(LEDC_CHANNEL_LCD_BACKLIGHT, UINT16_MAX);
  24. spr_.setColorDepth(8);
  25. if (spr_.createSprite(TFT_WIDTH, TFT_HEIGHT) == nullptr) {
  26. log("ERROR: sprite allocation failed!");
  27. tft_.fillScreen(TFT_RED);
  28. } else {
  29. log("Sprite created!");
  30. tft_.fillScreen(TFT_PURPLE);
  31. }
  32. spr_.setTextColor(0xFFFF, TFT_BLACK);
  33. PB_SmartKnobState state;
  34. const int RADIUS = TFT_WIDTH / 2;
  35. const uint16_t FILL_COLOR = spr_.color565(90, 18, 151);
  36. const uint16_t DOT_COLOR = spr_.color565(80, 100, 200);
  37. spr_.setTextDatum(CC_DATUM);
  38. spr_.setTextColor(TFT_WHITE);
  39. while(1) {
  40. if (xQueueReceive(knob_state_queue_, &state, portMAX_DELAY) == pdFALSE) {
  41. continue;
  42. }
  43. spr_.fillSprite(TFT_BLACK);
  44. int32_t num_positions = state.config.max_position - state.config.min_position + 1;
  45. if (num_positions > 1) {
  46. int32_t height = (state.current_position - state.config.min_position) * TFT_HEIGHT / (state.config.max_position - state.config.min_position);
  47. spr_.fillRect(0, TFT_HEIGHT - height, TFT_WIDTH, height, FILL_COLOR);
  48. }
  49. spr_.setFreeFont(&Roboto_Light_60);
  50. spr_.drawNumber(state.current_position, TFT_WIDTH / 2, TFT_HEIGHT / 2 - VALUE_OFFSET, 1);
  51. spr_.setFreeFont(&DESCRIPTION_FONT);
  52. int32_t line_y = TFT_HEIGHT / 2 + DESCRIPTION_Y_OFFSET;
  53. char* start = state.config.text;
  54. char* end = start + strlen(state.config.text);
  55. while (start < end) {
  56. char* newline = strchr(start, '\n');
  57. if (newline == nullptr) {
  58. newline = end;
  59. }
  60. char buf[sizeof(state.config.text)] = {};
  61. strncat(buf, start, min(sizeof(buf) - 1, (size_t)(newline - start)));
  62. spr_.drawString(String(buf), TFT_WIDTH / 2, line_y, 1);
  63. start = newline + 1;
  64. line_y += spr_.fontHeight(1);
  65. }
  66. float left_bound = PI / 2;
  67. if (num_positions > 0) {
  68. float range_radians = (state.config.max_position - state.config.min_position) * state.config.position_width_radians;
  69. left_bound = PI / 2 + range_radians / 2;
  70. float right_bound = PI / 2 - range_radians / 2;
  71. spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(left_bound), TFT_HEIGHT/2 - RADIUS * sinf(left_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(left_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(left_bound), TFT_WHITE);
  72. spr_.drawLine(TFT_WIDTH/2 + RADIUS * cosf(right_bound), TFT_HEIGHT/2 - RADIUS * sinf(right_bound), TFT_WIDTH/2 + (RADIUS - 10) * cosf(right_bound), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(right_bound), TFT_WHITE);
  73. }
  74. if (DRAW_ARC) {
  75. spr_.drawCircle(TFT_WIDTH/2, TFT_HEIGHT/2, RADIUS, TFT_DARKGREY);
  76. }
  77. float adjusted_sub_position = state.sub_position_unit * state.config.position_width_radians;
  78. if (num_positions > 0) {
  79. if (state.current_position == state.config.min_position && state.sub_position_unit < 0) {
  80. adjusted_sub_position = -logf(1 - state.sub_position_unit * state.config.position_width_radians / 5 / PI * 180) * 5 * PI / 180;
  81. } else if (state.current_position == state.config.max_position && state.sub_position_unit > 0) {
  82. adjusted_sub_position = logf(1 + state.sub_position_unit * state.config.position_width_radians / 5 / PI * 180) * 5 * PI / 180;
  83. }
  84. }
  85. float raw_angle = left_bound - (state.current_position - state.config.min_position) * state.config.position_width_radians;
  86. float adjusted_angle = raw_angle - adjusted_sub_position;
  87. if (num_positions > 0 && ((state.current_position == state.config.min_position && state.sub_position_unit < 0) || (state.current_position == state.config.max_position && state.sub_position_unit > 0))) {
  88. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(raw_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(raw_angle), 5, DOT_COLOR);
  89. if (raw_angle < adjusted_angle) {
  90. for (float r = raw_angle; r <= adjusted_angle; r += 2 * PI / 180) {
  91. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
  92. }
  93. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
  94. } else {
  95. for (float r = raw_angle; r >= adjusted_angle; r -= 2 * PI / 180) {
  96. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(r), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(r), 2, DOT_COLOR);
  97. }
  98. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 2, DOT_COLOR);
  99. }
  100. } else {
  101. spr_.fillCircle(TFT_WIDTH/2 + (RADIUS - 10) * cosf(adjusted_angle), TFT_HEIGHT/2 - (RADIUS - 10) * sinf(adjusted_angle), 5, DOT_COLOR);
  102. }
  103. spr_.pushSprite(0, 0);
  104. {
  105. SemaphoreGuard lock(mutex_);
  106. ledcWrite(LEDC_CHANNEL_LCD_BACKLIGHT, brightness_);
  107. }
  108. delay(2);
  109. }
  110. }
  111. QueueHandle_t DisplayTask::getKnobStateQueue() {
  112. return knob_state_queue_;
  113. }
  114. void DisplayTask::setBrightness(uint16_t brightness) {
  115. SemaphoreGuard lock(mutex_);
  116. brightness_ = brightness;
  117. }
  118. void DisplayTask::setLogger(Logger* logger) {
  119. logger_ = logger;
  120. }
  121. void DisplayTask::log(const char* msg) {
  122. if (logger_ != nullptr) {
  123. logger_->log(msg);
  124. }
  125. }
  126. #endif