Subversion Repositories DashDisplay

Rev

Rev 79 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * display.cpp
  3.  *
  4.  *  Created on: 30 Nov 2020
  5.  *      Author: mike
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include "main.h"
  10. #include "display.h"
  11. #include "switches.h"
  12. #include "nvram.h"
  13. #include <cstring>
  14. #include <cstdlib>
  15. #include "libOLED/stm32_halDisplay.H"
  16. #include "libOLED/fontclass.H"
  17. #include "libOLED/displayDial.H"
  18. #include "libPlx/displayInfo.H"
  19. #include "libOLED/ap_math.h"
  20. #include "libSmallPrintf/small_printf.h"
  21.  
  22. #include "splash.H"
  23.  
  24. namespace
  25. {
  26.         int const WIDTH = 128;
  27.         int const HEIGHT = 64;
  28.         int const DISPLAY_RAMWIDTH = 132;
  29. }
  30.  
  31. uint8_t displayBuffer[2][dataSize(WIDTH, HEIGHT)];
  32.  
  33. stm32_halDisplay_t displays[MAX_DISPLAYS] =
  34.         {stm32_halDisplay_t(WIDTH, HEIGHT, DISPLAY_RAMWIDTH, displayBuffer[0],
  35.                                                 &hspi1,
  36.                                                 SPI_CD_GPIO_Port,
  37.                                                 SPI_CD_Pin,
  38.                                                 SPI_RESET_GPIO_Port,
  39.                                                 SPI_RESET_Pin,
  40.                                                 SPI_NSS1_GPIO_Port,
  41.                                                 SPI_NSS1_Pin),
  42.          stm32_halDisplay_t(WIDTH, HEIGHT,
  43.                                                 DISPLAY_RAMWIDTH,
  44.                                                 displayBuffer[1],
  45.                                                 &hspi1,
  46.                                                 SPI_CD_GPIO_Port,
  47.                                                 SPI_CD_Pin,
  48.                                                 SPI_RESET_GPIO_Port,
  49.                                                 SPI_RESET_Pin,
  50.                                                 SPI_NSS2_GPIO_Port,
  51.                                                 SPI_NSS2_Pin)};
  52.  
  53. displayDial_t dials[MAX_DISPLAYS] =
  54.         {displayFullDial_t(displays[0]), displayFullDial_t(displays[1])};
  55. #if defined __cplusplus
  56. extern "C"
  57. {
  58. #endif
  59.         static void
  60.         showMinMax(display_t &display, uint8_t dp_pos, int16_t int_min,
  61.                            uint16_t int_max)
  62.         {
  63.                 const char padding[] = "      ";
  64.                 // left justified display of minimum
  65.                 int8_t width = display.fontSigDigits(small_font, 0, 0, 0, dp_pos, int_min, WHITE);
  66.                 // pad with spaces if fewer than 6 characters are used.
  67.                 if (width != 6)
  68.                         display.printString(small_font, padding, 6 - width, WHITE);
  69.  
  70.                 display.gotoxy(0, 8);
  71.                 display.printString(small_font, "Min", 3, WHITE);
  72.  
  73.                 // right justified display of maximum
  74.                 width = display.fontSigDigits(small_font, 120, 0, 1, dp_pos, int_max, WHITE);
  75.                 // right justified display of maximum : pad spaces to left
  76.                 if (width != 6)
  77.                         display.printString(small_font, padding, 6 - width, WHITE);
  78.  
  79.                 display.gotoxy(110, 8);
  80.                 display.printString(small_font, "Max", 3, WHITE);
  81.         }
  82.  
  83.         void
  84.         cc_init()
  85.         {
  86.                 for (auto i = 0; i < MAX_DISPLAYS; i++)
  87.                 {
  88.                         display_t &display = displays[i];
  89.                         if (i == 0)
  90.                                 display.reset();
  91.                         display.init();
  92.                         display.clearDisplay(BLACK);
  93.                         displaySplash(display);
  94.                         display.gotoxy(8, 32);
  95.                         display.printString(large_font, i == 0 ? "1" : "2", 1, BLACK);
  96.                         display.display();
  97.                 }
  98.  
  99.                 HAL_Delay(1000);
  100.  
  101.                 for (auto i = 0; i < MAX_DISPLAYS; i++)
  102.                 {
  103.                         display_t &display = displays[i];
  104.                         display.clearDisplay(BLACK);
  105.                         display.setPixelMode(WHITE);
  106.                         display.display();
  107.                         context_t &context = contexts[i];
  108.                         resetDialTimer(i);
  109.                         context.dial1 = -1;
  110.                         context.OldObservation = nullObs;
  111.  
  112.                         context.knobPos = -1; // indicate it is initialised
  113.                 }
  114.         }
  115.  
  116.         // Check to see if there is an observation/instance in the dynamic data array
  117.         // that matches the current observation/instance in the NVRAM
  118.  
  119.         void
  120.         cc_check_nvram(int dialIndex)
  121.         {
  122.                 if (dialIndex < 0 && dialIndex > MAX_DISPLAYS)
  123.                         return;
  124.                 // algorithm only works when there is a vector of observations
  125.  
  126.                 context_t &context = contexts[dialIndex];
  127.  
  128.                 // check for timer timeout using systick timer
  129.                 if ((context.dial_timer != 0) && (HAL_GetTick() > context.dial_timer))
  130.                 {
  131.                         contexts[dialIndex].dial_timer = 0; // used timer timeout
  132.                                                                                                 // use dialIndex+1 as tag for data : always non-zero.
  133. #if defined SEMIHOSTING
  134.                         printf("check %d\n", dialIndex);
  135. #endif
  136.                         nvram_info_t *dial_nvram = find_nvram_data(dialIndex + 1);
  137.  
  138.                         // initial read operation if knobPos < 0
  139.                         if (context.knobPos < 0)
  140.                         {
  141.                                 if (dial_nvram)
  142.                                 {
  143.                                         // found NVRAM content
  144.                                         context.knobPos = dial_nvram->data.pos;
  145.                                 }
  146.                                 else
  147.                                 // not found in NVRAM, so use default
  148.                                 {
  149.                                         context.knobPos = dialIndex;
  150.                                 }
  151.                                 return;
  152.                         }
  153.  
  154.                         // dont save dial info for invalid data
  155.                         if (!isValid(context.knobPos))
  156.                                 return;
  157.                         // is this a change since the last timeout ?
  158.  
  159.                         if (!dial_nvram || context.knobPos != dial_nvram->data.pos)
  160.                         {
  161.  
  162.                                 // store the observation and instance in the NVRAM, not dial position.
  163.                                 nvram_info_t curr_val;
  164.                                 curr_val.u32 = 0;
  165.                                 curr_val.data.pos = context.knobPos;
  166.                                 curr_val.data.tag = dialIndex + 1;
  167. #if defined SEMIHOSTING
  168.                                 printf("saving %d\n", dialIndex);
  169. #endif
  170.                                 write_nvram_data(curr_val);
  171.                         }
  172.                 }
  173.         }
  174.  
  175.         int
  176.         cc_display(int dialIndex, int suppressIndex)
  177.  
  178.         {
  179.  
  180.                 if (dialIndex < 0 && dialIndex > MAX_DISPLAYS)
  181.                         return -1;
  182.                 context_t &context = contexts[dialIndex];
  183.                 displayDial_t &dial = dials[dialIndex];
  184.                 stm32_halDisplay_t &display = displays[dialIndex];
  185.                 int itemIndex = context.knobPos;
  186.                 char buff[10];
  187.                 int i;
  188.                 const char *msg;
  189.                 int len;
  190.                 // check for startup phase
  191.                 if (itemIndex < 0)
  192.                 {
  193.                         display.clearDisplay(BLACK);
  194.                         i = small_sprintf(buff, "Wait");
  195.                         display.gotoxy(64 - i * 4, 48);
  196.                         display.printString(large_font, buff, i, WHITE);
  197.  
  198.                         display.display();
  199.                         return -1;
  200.                 }
  201.  
  202.                 // check for item suppression
  203.                 if (itemIndex == suppressIndex)
  204.                 {
  205.                         context.dial1 = -1;
  206.                         context.OldObservation = nullObs;
  207.                         display.clearDisplay(BLACK);
  208.                         i = small_sprintf(buff, "Supp-%02d", itemIndex);
  209.                         display.gotoxy(64 - i * 4, 48);
  210.                         display.printString(large_font, buff, i, WHITE);
  211.  
  212.                         display.display();
  213.                         return -1; // we suppressed this display
  214.                 }
  215.  
  216.                 // check for item validity
  217.                 if (!isValid(itemIndex))
  218.                 {
  219.                         context.dial1 = -1;
  220.                         context.OldObservation = nullObs;
  221.                         display.clearDisplay(BLACK);
  222.                         i = small_sprintf(buff, "Inval-%02d", itemIndex);
  223.                         display.gotoxy(64 - i * 4, 48);
  224.                         display.printString(large_font, buff, i, WHITE);
  225.  
  226.                         display.display();
  227.                         return itemIndex;
  228.                 }
  229.  
  230.                 // clear startup display off the screen
  231.                 if (context.OldObservation.Obs == -1)
  232.                         display.clearDisplay(BLACK);
  233.  
  234.                 int DataVal = Info[itemIndex].data; // data reading
  235.                 PLX_Observations Observation = Info[itemIndex].observation.Obs;
  236.                 uint8_t Instance = Info[itemIndex].observation.Instance;
  237.                 // now to convert the readings and format strings
  238.                 // find out limits
  239.                 // if the user presses the dial then reset min/max to current value
  240.                 if (push_pos[dialIndex] == 1)
  241.                 {
  242.                         Info[itemIndex].Max = DataVal;
  243.                         Info[itemIndex].Min = DataVal; // 12 bit max value
  244.                 }
  245.  
  246.                 // detect change in observation being displayed, reset the dial
  247.                 if (Observation < PLX_MAX_OBS)
  248.                 {
  249.                         if (Observation != context.OldObservation.Obs || Instance != context.OldObservation.Instance)
  250.                         {
  251.  
  252.                                 display.clearDisplay(BLACK);
  253.                                 dial.draw_scale(DisplayInfo[Observation].Low,
  254.                                                                 DisplayInfo[Observation].High, 12, 1,
  255.                                                                 DisplayInfo[Observation].TickScale);
  256.  
  257.                                 dial.draw_limits();
  258.  
  259.                                 msg = DisplayInfo[Observation].name;
  260.                                 len = 7;
  261.                                 int len1 = Instance > 0 ? len - 1 : len;
  262.                                 for (i = 0; i < len1 && msg[i]; i++)
  263.                                 {
  264.                                         buff[i] = msg[i];
  265.                                 }
  266.                                 if (Instance > 0 && i < len)
  267.                                 {
  268.                                         buff[i++] = Instance + '1';
  269.                                 }
  270.  
  271.                                 display.gotoxy(64 - i * 4, 48);
  272.                                 display.printString(large_font, buff, i, WHITE);
  273.  
  274.                                 context.OldObservation.Obs = Observation;
  275.                                 context.OldObservation.Instance = Instance;
  276.                                 context.dial1 = -1; // do not display old needle, cleared screen
  277.                                 display.display();
  278.                         }
  279.                 }
  280.  
  281.                 if (Info[itemIndex].updated)
  282.                 {
  283.                         Info[itemIndex].updated = 0;
  284.  
  285.                         double max_rdg;
  286.                         double min_rdg;
  287.                         double cur_rdg;
  288.                         int int_rdg;
  289.                         int int_max;
  290.                         int int_min;
  291.  
  292.                         max_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
  293.                                                                                  Info[itemIndex].Max);
  294.                         min_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
  295.                                                                                  Info[itemIndex].Min);
  296.                         cur_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
  297.                                                                                  Info[itemIndex].data);
  298.                         int dp_pos; // where to print the decimal place
  299.                         float scale = 1.0;
  300.                         switch (DisplayInfo[Observation].DP)
  301.                         {
  302.                         default:
  303.                         case 0:
  304.                                 scale = 1.0;
  305.                                 dp_pos = display_t::NO_DECIMAL;
  306.                                 break;
  307.                         case 1:
  308.                                 scale = 10.0;
  309.                                 dp_pos = 1;
  310.                                 break;
  311.                         case 2:
  312.                                 scale = 100.0;
  313.                                 dp_pos = 2;
  314.                                 break;
  315.                         }
  316.                         int_rdg = (int)(cur_rdg * scale);
  317.                         int_max = (int)(max_rdg * scale);
  318.                         int_min = (int)(min_rdg * scale);
  319.  
  320.                         cur_rdg -= DisplayInfo[Observation].Low;
  321.                         cur_rdg = ap_math::SINE_STEPS * cur_rdg / (DisplayInfo[Observation].High - DisplayInfo[Observation].Low);
  322.  
  323.                         context.dial0 = (int)cur_rdg;
  324.  
  325.                         display.gotoxy(32, 28);
  326.                         display.fontDigits(large_font, 4, dp_pos, int_rdg, WHITE);
  327.  
  328.                         display.printString(small_font, DisplayInfo[Observation].suffix,
  329.                                                                 strlen(DisplayInfo[Observation].suffix));
  330.                         display.printString(small_font, "    ",
  331.                                                                 3 - strlen(DisplayInfo[Observation].suffix));
  332.                         // print value overlaid by needle
  333.  
  334.                         /* old needle un-draw */
  335.                         if (context.dial1 >= 0)
  336.                         {
  337.                                 dial.draw_needle(context.dial1);
  338.                         }
  339.                         dial.draw_needle(context.dial0);
  340.                         context.dial1 = context.dial0;
  341.                         showMinMax(display, dp_pos, int_min, int_max);
  342.                 }
  343.                 else
  344.                 {
  345.                         if (Info[itemIndex].lastUpdated && ((HAL_GetTick() - Info[itemIndex].lastUpdated) > 1000))
  346.                         {
  347.                                 context.OldObservation = nullObs; // force a redraw on next update
  348.                                 Info[itemIndex].lastUpdated = 0;  // and stop further timeouts.
  349.                         }
  350.                 }
  351.  
  352.                 display.gotoxy(0, 32);
  353.  
  354.                 // display BT connection status
  355.                 display.printString(small_font, btConnected() ? "\x81" : " ", 1);
  356.  
  357.                 display.display();
  358.  
  359.                 return itemIndex;
  360.         }
  361. }
  362.