display.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*
  2. * (c) 2004,2006 Richard Titmuss for SlimProtoLib
  3. * (c) Philippe G. 2019, philippe_44@outlook.com
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #include <string.h>
  20. #include <ctype.h>
  21. #include <stdint.h>
  22. #include <arpa/inet.h>
  23. #include "esp_log.h"
  24. #include "config.h"
  25. #include "display.h"
  26. // here we should include all possible drivers
  27. extern struct display_s SSD1306_display;
  28. struct display_s *display;
  29. static const char *TAG = "display";
  30. #define min(a,b) (((a) < (b)) ? (a) : (b))
  31. #define DISPLAYER_STACK_SIZE 2048
  32. #define SCROLLABLE_SIZE 384
  33. static EXT_RAM_ATTR struct {
  34. TaskHandle_t task;
  35. SemaphoreHandle_t mutex;
  36. int pause, speed, by;
  37. enum { DISPLAYER_DISABLED, DISPLAYER_IDLE, DISPLAYER_ACTIVE } state;
  38. char string[SCROLLABLE_SIZE + 1];
  39. int offset, boundary;
  40. } displayer;
  41. static void displayer_task(void *args);
  42. /****************************************************************************************
  43. *
  44. */
  45. void display_init(char *welcome) {
  46. bool init = false;
  47. char *item = config_alloc_get(NVS_TYPE_STR, "display_config");
  48. if (item && *item) {
  49. char * drivername=strstr(item,"driver");
  50. if (!drivername || (drivername && strcasestr(drivername,"SSD1306"))) {
  51. display = &SSD1306_display;
  52. if (display->init(item, welcome)) {
  53. init = true;
  54. ESP_LOGI(TAG, "Display initialization successful");
  55. } else {
  56. ESP_LOGE(TAG, "Display initialization failed");
  57. }
  58. } else {
  59. ESP_LOGE(TAG,"Unknown display driver name in display config: %s",item);
  60. }
  61. } else {
  62. ESP_LOGW(TAG, "no display");
  63. }
  64. if (init) {
  65. static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
  66. static EXT_RAM_ATTR StackType_t xStack[DISPLAYER_STACK_SIZE] __attribute__ ((aligned (4)));
  67. // start the task that will handle scrolling & counting
  68. displayer.mutex = xSemaphoreCreateMutex();
  69. displayer.by = 2;
  70. displayer.pause = 3600;
  71. displayer.speed = 33;
  72. displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", DISPLAYER_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
  73. // set lines for "fixed" text mode
  74. display->set_font(1, DISPLAY_FONT_TINY, 0);
  75. display->set_font(2, DISPLAY_FONT_LARGE, 0);
  76. }
  77. if (item) free(item);
  78. }
  79. /****************************************************************************************
  80. *
  81. */
  82. static void displayer_task(void *args) {
  83. while (1) {
  84. int scroll_pause = displayer.pause;
  85. // suspend ourselves if nothing to do
  86. if (displayer.state == DISPLAYER_DISABLED) {
  87. vTaskSuspend(NULL);
  88. display->clear();
  89. }
  90. // something to scroll (or we'll wake-up every pause ms ... no big deal)
  91. if (*displayer.string && displayer.state == DISPLAYER_ACTIVE) {
  92. display->line(2, DISPLAY_LEFT, -displayer.offset, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.string);
  93. xSemaphoreTake(displayer.mutex, portMAX_DELAY);
  94. scroll_pause = displayer.offset ? displayer.speed : displayer.pause;
  95. displayer.offset = displayer.offset >= displayer.boundary ? 0 : (displayer.offset + min(displayer.by, displayer.boundary - displayer.offset));
  96. xSemaphoreGive(displayer.mutex);
  97. } else if (displayer.state == DISPLAYER_IDLE) {
  98. display->line(2, DISPLAY_LEFT, 0, DISPLAY_CLEAR | DISPLAY_UPDATE, displayer.string);
  99. scroll_pause = displayer.pause;
  100. }
  101. vTaskDelay(scroll_pause / portTICK_PERIOD_MS);
  102. }
  103. }
  104. /****************************************************************************************
  105. * This is not really thread-safe as displayer_task might be in the middle of line drawing
  106. * but it won't crash (I think) and making it thread-safe would be complicated for a
  107. * feature which is secondary (the LMS version of scrolling is thread-safe)
  108. */
  109. void displayer_scroll(char *string, int speed) {
  110. xSemaphoreTake(displayer.mutex, portMAX_DELAY);
  111. if (speed) displayer.speed = speed;
  112. displayer.offset = 0;
  113. displayer.state = DISPLAYER_ACTIVE;
  114. strncpy(displayer.string, string, SCROLLABLE_SIZE);
  115. displayer.string[SCROLLABLE_SIZE] = '\0';
  116. displayer.boundary = display->stretch(2, displayer.string, SCROLLABLE_SIZE);
  117. xSemaphoreGive(displayer.mutex);
  118. }
  119. /****************************************************************************************
  120. * See above comment
  121. */
  122. void displayer_control(enum displayer_cmd_e cmd) {
  123. xSemaphoreTake(displayer.mutex, portMAX_DELAY);
  124. displayer.offset = 0;
  125. displayer.boundary = 0;
  126. switch(cmd) {
  127. case DISPLAYER_SHUTDOWN:
  128. displayer.state = DISPLAYER_DISABLED;
  129. vTaskSuspend(displayer.task);
  130. break;
  131. case DISPLAYER_ACTIVATE:
  132. displayer.state = DISPLAYER_ACTIVE;
  133. displayer.string[0] = '\0';
  134. vTaskResume(displayer.task);
  135. break;
  136. case DISPLAYER_SUSPEND:
  137. displayer.state = DISPLAYER_IDLE;
  138. break;
  139. default:
  140. break;
  141. }
  142. xSemaphoreGive(displayer.mutex);
  143. }