Subversion Repositories DashDisplay

Rev

Rev 75 | Rev 77 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /* USER CODE BEGIN Header */
  2. /**
  3.  ******************************************************************************
  4.  * @file           : main.c
  5.  * @brief          : Main program body
  6.  ******************************************************************************
  7.  * @attention
  8.  *
  9.  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  10.  * All rights reserved.</center></h2>
  11.  *
  12.  * This software component is licensed by ST under BSD 3-Clause license,
  13.  * the "License"; You may not use this file except in compliance with the
  14.  * License. You may obtain a copy of the License at:
  15.  *                        opensource.org/licenses/BSD-3-Clause
  16.  *
  17.  ******************************************************************************
  18.  */
  19. /* USER CODE END Header */
  20. /* Includes ------------------------------------------------------------------*/
  21. #include "main.h"
  22.  
  23. #include <stdlib.h>
  24.  
  25. /* Private includes ----------------------------------------------------------*/
  26. /* USER CODE BEGIN Includes */
  27.  
  28. #include "libPLX/plx.h"
  29. #include "libPLX/displayinfo.h"
  30. #include "libPLX/commsLib.h"
  31. #include "libSerial/serialUtils.H"
  32.  
  33. #include "libSmallPrintf/small_printf.h"
  34. #include "libNMEA/nmea.h"
  35. #include "switches.h"
  36. #include <string.h>
  37.  
  38. /* USER CODE END Includes */
  39.  
  40. /* Private typedef -----------------------------------------------------------*/
  41. /* USER CODE BEGIN PTD */
  42.  
  43. /* USER CODE END PTD */
  44.  
  45. /* Private define ------------------------------------------------------------*/
  46. /* USER CODE BEGIN PD */
  47. /* USER CODE END PD */
  48.  
  49. /* Private macro -------------------------------------------------------------*/
  50. /* USER CODE BEGIN PM */
  51.  
  52. /* USER CODE END PM */
  53.  
  54. /* Private variables ---------------------------------------------------------*/
  55. I2C_HandleTypeDef hi2c1;
  56.  
  57. SPI_HandleTypeDef hspi1;
  58.  
  59. TIM_HandleTypeDef htim2;
  60. TIM_HandleTypeDef htim3;
  61. TIM_HandleTypeDef htim9;
  62.  
  63. UART_HandleTypeDef huart4;
  64. UART_HandleTypeDef huart1;
  65. UART_HandleTypeDef huart2;
  66. UART_HandleTypeDef huart3;
  67.  
  68. /* USER CODE BEGIN PV */
  69. /* Private variables ---------------------------------------------------------*/
  70.  
  71. ///@brief  timeout when the ignition is switched off
  72. uint32_t const IGNITION_OFF_TIMEOUT = 30000UL;
  73.  
  74. /// @brief 1000mS per logger period, print average per period
  75. uint32_t const LOGGER_INTERVAL = 250UL;
  76.  
  77. /// @brief  about 10 seconds after twiddle, save the dial position.
  78. const int DialTimeout = 100;
  79.  
  80. /// @brief Unused Observation
  81. uniqueObs_t const nullObs = {PLX_MAX_OBS,
  82.                              PLX_MAX_INST};
  83.  
  84. /// @brief Null context
  85. context_t const nullContext = {.knobPos = -1,
  86.                                .dial_timer = 0,
  87.                                .dial0 = -1,
  88.                                .dial1 = -1,
  89.                                .OldObservation = nullObs};
  90.  
  91. /// @brief Define a null item
  92. info_t const nullInfo = {.Max = 0,
  93.                          .Min = 0xFFF,
  94.                          .sum = 0,
  95.                          .count = 0,
  96.                          .updated = 0,
  97.                          .lastUpdated = 0,
  98.                          .observation = nullObs};
  99.  
  100. context_t contexts[MAX_DISPLAYS];
  101.  
  102. /// @brief Data storage for readings
  103. info_t Info[MAXRDG];
  104.  
  105. uint32_t Latch_Timer;
  106.  
  107. // location for GPS data
  108. Location loc;
  109.  
  110. /// @brief Time when the logged data will be sent
  111. uint32_t nextTickReload;
  112.  
  113. // data timeout
  114. uint32_t dataTimeout = 0; //
  115.  
  116. // USART buffers
  117. uint8_t uc1_tx_buffer[TX_USART_BUFF_SIZ];
  118. uint8_t uc1_rx_buffer[RX_USART_BUFF_SIZ];
  119.  
  120. uint8_t uc2_tx_buffer[TX_USART_BUFF_SIZ];
  121. uint8_t uc2_rx_buffer[RX_USART_BUFF_SIZ];
  122.  
  123. uint8_t uc3_tx_buffer[TX_USART_BUFF_SIZ];
  124. uint8_t uc3_rx_buffer[RX_USART_BUFF_SIZ];
  125.  
  126. uint8_t uc4_tx_buffer[TX_USART_BUFF_SIZ];
  127. uint8_t uc4_rx_buffer[RX_USART_BUFF_SIZ];
  128.  
  129. /* USER CODE END PV */
  130.  
  131. /* Private function prototypes -----------------------------------------------*/
  132. void SystemClock_Config(void);
  133. static void MX_GPIO_Init(void);
  134. static void MX_SPI1_Init(void);
  135. static void MX_USART1_UART_Init(void);
  136. static void MX_USART2_UART_Init(void);
  137. static void MX_USART3_UART_Init(void);
  138. static void MX_TIM3_Init(void);
  139. static void MX_TIM9_Init(void);
  140. static void MX_TIM2_Init(void);
  141. static void MX_UART4_Init(void);
  142. static void MX_I2C1_Init(void);
  143. /* USER CODE BEGIN PFP */
  144.  
  145. // the dial is the switch number we are using.
  146. // suppress is the ItemIndex we wish to suppress on this display
  147. int DisplayCurrent(int dial, int suppress)
  148. {
  149.   return cc_display(dial, suppress);
  150. }
  151.  
  152. /// \note  HC-05 only accepts : 9600,19200,38400,57600,115200,230400,460800 baud
  153. /// \brief Setup Bluetooth module
  154. void initModule(struct usart_ctl *ctl, uint32_t baudRate)
  155. {
  156.   char initBuf[60];
  157.   // switch to command mode
  158.   HAL_GPIO_WritePin(BT_RESET_GPIO_Port, BT_RESET_Pin, GPIO_PIN_SET);
  159.   HAL_Delay(500);
  160.   // clear the button press
  161.   HAL_GPIO_WritePin(BT_RESET_GPIO_Port, BT_RESET_Pin, GPIO_PIN_RESET);
  162.   HAL_Delay(500);
  163.   setBaud(ctl, 38400);
  164.   int initLen = small_sprintf(initBuf, "AT\nAT+UART?\nAT+UART=%ld,0,0\n", baudRate);
  165.   const char buf[] = "AT+RESET\n";
  166.   sendString(ctl, initBuf, initLen);
  167.   HAL_Delay(500);
  168.   initLen = small_sprintf(initBuf, buf);
  169.   sendString(ctl, initBuf, initLen);
  170.  
  171.   TxWaitEmpty(ctl);
  172.  
  173.   // switch back to normal comms at new baud rate
  174.   setBaud(ctl, baudRate);
  175.   HAL_Delay(100);
  176. }
  177.  
  178. // workspace for RMC data read from GPS module.
  179. volatile uint16_t rmc_length;
  180.  
  181. uint8_t rmc_callback(uint8_t *data, uint16_t length)
  182. {
  183.   // send it back out
  184.   rmc_length = length;
  185.  
  186.   sendString(&uc3, (const char *)data, length);
  187.  
  188.   nextTickReload = HAL_GetTick() + LOGGER_INTERVAL;
  189.  
  190.   return 0;
  191. }
  192.  
  193. // check if bluetooth connected
  194. uint8_t btConnected()
  195. {
  196.   return HAL_GPIO_ReadPin(BT_STATE_GPIO_Port, BT_STATE_Pin) == GPIO_PIN_SET;
  197. }
  198.  
  199. /// @brief return true if this slot is unused
  200. /// @param ptr pointer to the slot to
  201. uint8_t isUnused(int index)
  202. {
  203.   if (index < 0 || index > MAXRDG)
  204.     return false;
  205.  
  206.   return Info[index].observation.Instance == PLX_MAX_INST && Info[index].observation.Obs == PLX_MAX_OBS;
  207. }
  208.  
  209. /// @brief Determine if an entry is currently valid
  210. /// @param index the number of the array entry to display
  211. /// @return true if the entry contains data which is fresh
  212. uint8_t isValid(int index)
  213. {
  214.   if (index < 0 || index > MAXRDG)
  215.     return false;
  216.   if (isUnused(index))
  217.     return false;
  218.  
  219.   uint32_t age = HAL_GetTick() - Info[index].lastUpdated;
  220.  
  221.   if (age > 300)
  222.     return false;
  223.  
  224.   return true;
  225. }
  226.  
  227. /* USER CODE END PFP */
  228.  
  229. /* Private user code ---------------------------------------------------------*/
  230. /* USER CODE BEGIN 0 */
  231. void libPLXcallbackSendUserData(struct usart_ctl * instance)
  232. {
  233.   (void)instance;
  234. }
  235.  
  236.  
  237. void libPLXcallbackRecievedData(PLX_SensorInfo *info)
  238. {
  239.   // received some data , timeout is reset
  240.   dataTimeout = 0;
  241.  
  242.   // search to see if the item already has a slot in the Info[] array
  243.   // match the observation and instance: if found, update entry
  244.   enum PLX_Observations observation = ConvPLX(info->AddrH,
  245.                                               info->AddrL);
  246.  
  247.   char instance = info->Instance;
  248.  
  249.   int16_t data = ConvPLX(info->ReadingH,
  250.                          info->ReadingL);
  251.  
  252.   // validate the current item, discard out of range
  253.   if ((instance > PLX_MAX_INST) || (observation > PLX_MAX_OBS))
  254.     return;
  255.  
  256.   // search for the item in the list
  257.   int currentSlot;
  258.   for (currentSlot = 0; currentSlot < MAXRDG; ++currentSlot)
  259.   {
  260.     if ((Info[currentSlot].observation.Obs == observation) && (Info[currentSlot].observation.Instance == instance))
  261.       break;
  262.   }
  263.   // fallen off the end of the list of existing items without a match, so j points at next new item
  264.   //
  265.   // Find an unused slot
  266.  
  267.   if (currentSlot == MAXRDG)
  268.   {
  269.     int k;
  270.     {
  271.       for (k = 0; k < MAXRDG; ++k)
  272.         if (!isValid(k))
  273.         {
  274.           currentSlot = k; // found a spare slot
  275.           Info[currentSlot] = nullInfo;
  276.           break;
  277.         }
  278.     }
  279.     if (k == MAXRDG)
  280.       return; // abandon this iteration
  281.   }
  282.  
  283.   // give up if we are going to fall off the end of the array
  284.   if (currentSlot >= MAXRDG)
  285.     return;
  286.  
  287.   Info[currentSlot].observation.Obs = observation;
  288.  
  289.   Info[currentSlot].observation.Instance = instance;
  290.   Info[currentSlot].data = data;
  291.   if (data > Info[currentSlot].Max)
  292.   {
  293.     Info[currentSlot].Max = data;
  294.   }
  295.   if (data < Info[currentSlot].Min)
  296.   {
  297.     Info[currentSlot].Min = data;
  298.   }
  299.   // take an average
  300.   Info[currentSlot].sum += data;
  301.   Info[currentSlot].count++;
  302.   // note the last update time
  303.   Info[currentSlot].lastUpdated = HAL_GetTick();
  304.   Info[currentSlot].updated = 1; // it has been updated
  305.  
  306.   // scan through and invalidate all old items
  307.   for (int i = 0; i < MAXRDG; ++i)
  308.   {
  309.     if (!isValid(i))
  310.       Info[i] = nullInfo;
  311.   }
  312. }
  313. /* USER CODE END 0 */
  314.  
  315. /**
  316.  * @brief  The application entry point.
  317.  * @retval int
  318.  */
  319. int main(void)
  320. {
  321.   /* USER CODE BEGIN 1 */
  322.   __HAL_RCC_SPI1_CLK_ENABLE();
  323.   __HAL_RCC_USART1_CLK_ENABLE(); // PLX main port
  324.   __HAL_RCC_USART2_CLK_ENABLE(); // debug port
  325.   __HAL_RCC_USART3_CLK_ENABLE(); // Bluetooth port
  326.   __HAL_RCC_UART4_CLK_ENABLE();  // NMEA0183 port
  327.  
  328.   __HAL_RCC_TIM3_CLK_ENABLE();
  329.  
  330.   __HAL_RCC_TIM9_CLK_ENABLE();
  331.  
  332.   /* USER CODE END 1 */
  333.  
  334.   /* MCU Configuration--------------------------------------------------------*/
  335.  
  336.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  337.   HAL_Init();
  338.  
  339.   /* USER CODE BEGIN Init */
  340.  
  341.   /* USER CODE END Init */
  342.  
  343.   /* Configure the system clock */
  344.   SystemClock_Config();
  345.  
  346.   /* USER CODE BEGIN SysInit */
  347.   // Switch handler called on sysTick interrupt.
  348.   InitSwitches();
  349.  
  350.   /* USER CODE END SysInit */
  351.  
  352.   /* Initialize all configured peripherals */
  353.   MX_GPIO_Init();
  354.   MX_SPI1_Init();
  355.   MX_USART1_UART_Init();
  356.   MX_USART2_UART_Init();
  357.   MX_USART3_UART_Init();
  358.   MX_TIM3_Init();
  359.   MX_TIM9_Init();
  360.   MX_TIM2_Init();
  361.   MX_UART4_Init();
  362.   MX_I2C1_Init();
  363.   /* USER CODE BEGIN 2 */
  364.  
  365.   /* Turn on USART1 IRQ */
  366.   HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
  367.   HAL_NVIC_EnableIRQ(USART1_IRQn);
  368.  
  369.   /* Turn on USART2 IRQ  */
  370.   HAL_NVIC_SetPriority(USART2_IRQn, 4, 0);
  371.   HAL_NVIC_EnableIRQ(USART2_IRQn);
  372.  
  373.   /* turn on USART3 IRQ */
  374.   HAL_NVIC_SetPriority(USART3_IRQn, 4, 0);
  375.   HAL_NVIC_EnableIRQ(USART3_IRQn);
  376.  
  377.   /* turn on UART4 IRQ */
  378.   HAL_NVIC_SetPriority(UART4_IRQn, 4, 0);
  379.   HAL_NVIC_EnableIRQ(UART4_IRQn);
  380.  
  381.   /* setup the USART control blocks */
  382.   init_usart_ctl(&uc1, &huart1, uc1_tx_buffer,
  383.                  uc1_rx_buffer,
  384.                  TX_USART_BUFF_SIZ,
  385.                  TX_USART_BUFF_SIZ);
  386.   init_usart_ctl(&uc2, &huart2, uc2_tx_buffer,
  387.                  uc2_rx_buffer,
  388.                  TX_USART_BUFF_SIZ,
  389.                  TX_USART_BUFF_SIZ);
  390.   init_usart_ctl(&uc3, &huart3, uc3_tx_buffer,
  391.                  uc3_rx_buffer,
  392.                  TX_USART_BUFF_SIZ,
  393.                  TX_USART_BUFF_SIZ);
  394.   init_usart_ctl(&uc4, &huart4, uc4_tx_buffer,
  395.                  uc4_rx_buffer,
  396.                  TX_USART_BUFF_SIZ,
  397.                  TX_USART_BUFF_SIZ);
  398.  
  399.   EnableSerialRxInterrupt(&uc1);
  400.   EnableSerialRxInterrupt(&uc2);
  401.   EnableSerialRxInterrupt(&uc3);
  402.   EnableSerialRxInterrupt(&uc4);
  403.  
  404.   HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
  405.  
  406.   HAL_TIM_Encoder_Start(&htim9, TIM_CHANNEL_ALL);
  407.  
  408.   initModule(&uc3, 38400);
  409.  
  410.   // Initialise UART for 4800 baud NMEA
  411.   setBaud(&uc2, 4800);
  412.  
  413.   // Initialuse UART4 for 4800 baud NMEA.
  414.   setBaud(&uc4, 4800);
  415.  
  416.   cc_init();
  417.  
  418.   for (int i = 0; i < MAX_DISPLAYS; ++i)
  419.   {
  420.     contexts[i] = nullContext; // set the knob position
  421.   }
  422.  
  423.   /* reset the display timeout, latch on power from accessories */
  424.   Latch_Timer = IGNITION_OFF_TIMEOUT;
  425.   HAL_GPIO_WritePin(POWER_LATCH_GPIO_Port, POWER_LATCH_Pin, GPIO_PIN_RESET);
  426.  
  427.   /// @brief Time when the logged data will be sent
  428.  
  429.   setRmcCallback(&rmc_callback);
  430.  
  431.   // used in NMEA style logging
  432.   uint32_t nextTick = 0; ///< time to send next
  433.   nextTickReload = 0;
  434.   uint32_t offsetTicks = 0; ///< time to print as offset in mS for each loop
  435.  
  436.  
  437.   for (int i = 0; i < MAXRDG; ++i)
  438.   {
  439.     Info[i] = nullInfo;
  440.   }
  441.  
  442.   uint32_t resetCounter = 0; // record time at which both reset buttons were first pressed.
  443.  
  444.   resetPLX();
  445.   /* USER CODE END 2 */
  446.  
  447.   /* Infinite loop */
  448.   /* USER CODE BEGIN WHILE */
  449.   while (1)
  450.   {
  451.  
  452.     /* while ignition is on, keep resetting power latch timer */
  453.     if (HAL_GPIO_ReadPin(IGNITION_GPIO_Port, IGNITION_Pin) == GPIO_PIN_RESET)
  454.     {
  455.       Latch_Timer = HAL_GetTick() + IGNITION_OFF_TIMEOUT;
  456.     }
  457.     else
  458.     {
  459.       /* if the ignition has been off for a while, then turn off power */
  460.       if (HAL_GetTick() > Latch_Timer)
  461.       {
  462.         HAL_GPIO_WritePin(POWER_LATCH_GPIO_Port, POWER_LATCH_Pin,
  463.                           GPIO_PIN_RESET);
  464.       }
  465.     }
  466.  
  467.     // Handle the bluetooth pairing / reset function by pressing both buttons.
  468.     if ((push_pos[0] == 1) && (push_pos[1] == 1))
  469.     {
  470.       HAL_GPIO_WritePin(BT_BUTTON_GPIO_Port, BT_BUTTON_Pin,
  471.                         GPIO_PIN_RESET);
  472.       if (resetCounter == 0)
  473.         resetCounter = HAL_GetTick();
  474.     }
  475.     else
  476.     {
  477.       HAL_GPIO_WritePin(BT_BUTTON_GPIO_Port, BT_BUTTON_Pin,
  478.                         GPIO_PIN_SET);
  479.  
  480.       if (resetCounter != 0)
  481.       {
  482.         // Held down reset button for 10 seconds, clear NVRAM.
  483.         if ((HAL_GetTick() - resetCounter) > 10000)
  484.         {
  485.           for (int i = 0; i < MAX_DISPLAYS; i++)
  486.           {
  487.             contexts[i] = nullContext;
  488.             contexts[i].dial_timer = 1; // timeout immediately when decremented
  489.           }
  490.           erase_nvram();
  491.         }
  492.         resetCounter = 0;
  493.       }
  494.     }
  495.  
  496.     // poll GPS Position/time on UART4
  497.     (void)updateLocation(&loc, &uc4);
  498.     if (loc.valid == 'V')
  499.       memset(loc.time, '-', 6);
  500.  
  501.     // if permitted, log data from RMC packet
  502.     if (btConnected())
  503.     {
  504.       // Timeout for data logging regularly
  505.       if (HAL_GetTick() > nextTick)
  506.       {
  507.         nextTick = nextTickReload;
  508.         nextTickReload += LOGGER_INTERVAL;
  509.  
  510.         // Send items  to BT if it is in connected state
  511.         // print timestamp as a $PLTIM record.
  512.         char linebuff[20];
  513.         strftime(linebuff, sizeof(linebuff), "%H%M%S", &loc.tv);
  514.  
  515.         char outbuff[100];
  516.         int cnt = small_sprintf(outbuff, "$PLTIM,%s.%03lu\n", linebuff, offsetTicks);
  517.         sendString(&uc3, outbuff, cnt);
  518.         offsetTicks += LOGGER_INTERVAL;
  519.  
  520.         // increment timer
  521.         if (offsetTicks >= (1000))
  522.         {
  523.           offsetTicks -= 1000;
  524.           loc.tv.tm_sec++;
  525.           if (loc.tv.tm_sec >= 60)
  526.           {
  527.             loc.tv.tm_sec = 0;
  528.             loc.tv.tm_min++;
  529.             if (loc.tv.tm_min >= 60)
  530.             {
  531.               loc.tv.tm_hour++;
  532.               if (loc.tv.tm_hour >= 24)
  533.                 loc.tv.tm_hour = 0;
  534.             }
  535.           }
  536.         }
  537.  
  538.         for (int i = 0; i < MAXRDG; ++i)
  539.         {
  540.           if (!isValid(i))
  541.             continue;
  542.           // format output
  543.           // avoid division by zero for items with no sample data this iteration
  544.           if (Info[i].count == 0)
  545.             continue;
  546.           double average = (double)Info[i].sum / Info[i].count;
  547.           enum PLX_Observations Observation = Info[i].observation.Obs;
  548.  
  549.           double cur_rdg = ConveriMFDRaw2Data((enum PLX_Observations)Observation, DisplayInfo[Observation].Units,
  550.                                               average);
  551.           int cnt;
  552.           int intPart;
  553.           // depending on digits after the decimal point,
  554.           // choose how to format data
  555.           switch (DisplayInfo[Observation].DP)
  556.           {
  557.           default:
  558.           case 0:
  559.             cnt = small_sprintf(outbuff,
  560.                                 "$PLLOG,%s,%d,%d",
  561.                                 DisplayInfo[Info[i].observation.Obs].name,
  562.                                 Info[i].observation.Instance,
  563.                                 (int)cur_rdg);
  564.  
  565.             break;
  566.           case 1:
  567.             intPart = (int)(cur_rdg * 10);
  568.             cnt = small_sprintf(outbuff,
  569.                                 "$PLLOG,%s,%d,%d.%1d",
  570.                                 DisplayInfo[Info[i].observation.Obs].name,
  571.                                 Info[i].observation.Instance,
  572.                                 intPart / 10, abs(intPart) % 10);
  573.  
  574.             break;
  575.           case 2:
  576.             intPart = (int)(cur_rdg * 100);
  577.             cnt = small_sprintf(outbuff,
  578.                                 "$PLLOG,%s,%d,%d.%02d",
  579.                                 DisplayInfo[Info[i].observation.Obs].name,
  580.                                 Info[i].observation.Instance,
  581.                                 intPart / 100, abs(intPart) % 100);
  582.  
  583.             break;
  584.           }
  585.  
  586.           Info[i].count = 0;
  587.           Info[i].sum = 0;
  588.  
  589.           // NMEA style checksum
  590.           int ck;
  591.           int sum = 0;
  592.           for (ck = 1; ck < cnt; ck++)
  593.             sum += outbuff[ck];
  594.           cnt += small_sprintf(outbuff + cnt, "*%02X\n",
  595.                                sum & 0xFF);
  596.           sendString(&uc3, outbuff, cnt);
  597.         }
  598.       }
  599.     }
  600.  
  601.     // poll data into libPLX
  602.     libPLXpollData(&uc1);
  603.  
  604.     // determine if we are getting any data from the interface
  605.  
  606.     dataTimeout++;
  607.     if (btConnected() && (dataTimeout % 1000 == 0))
  608.     {
  609.       const char msg[] = "Timeout\r\n";
  610.       sendString(&uc3, msg, sizeof(msg));
  611.     }
  612.  
  613.     if (dataTimeout > 60000)
  614.     {
  615.  
  616.       // do turn off screen
  617.     }
  618.     // wait for a bit if nothing came in.
  619.     HAL_Delay(1);
  620.   }
  621.  
  622.   // handle switch rotation
  623.   for (int i = 0; i < MAX_DIALS; ++i)
  624.   {
  625.     int delta = get_dial_diff(i);
  626.     int pos = contexts[i].knobPos;
  627.     if (pos < 0)
  628.       break; // dont process until we have read NVRAM for the first time .
  629.     int start = pos;
  630.     // move in positive direction
  631.     while (delta > 0)
  632.     {
  633.       // skip invalid items, dont count
  634.       if (pos < MAXRDG - 1)
  635.         pos++;
  636.       else
  637.         pos = 0;
  638.  
  639.       if (isValid(pos))
  640.         delta--; // count a valid item
  641.  
  642.       // wrap
  643.       if (pos == start)
  644.         break;
  645.     }
  646.  
  647.     // move in negative direction
  648.     while (delta < 0)
  649.     {
  650.       // skip invalid items, dont count
  651.       if (pos > 0)
  652.         pos--;
  653.       else
  654.         pos = MAXRDG - 1;
  655.  
  656.       if (isValid(pos))
  657.         delta++; // count a valid item
  658.  
  659.       // wrap
  660.       if (pos == start)
  661.         break;
  662.     }
  663.  
  664.     contexts[i].knobPos = pos;
  665.     if (pos != start)
  666.       contexts[i].dial_timer = DialTimeout;
  667.   }
  668.  
  669.   int suppress = -1;
  670.   for (int i = 0; i < MAX_DISPLAYS; ++i)
  671.   { // now to display the information
  672.     suppress = DisplayCurrent(i, suppress);
  673.  
  674.     cc_check_nvram(i);
  675.   }
  676.   /* USER CODE END WHILE */
  677.  
  678.   /* USER CODE BEGIN 3 */
  679.  
  680.   /* USER CODE END 3 */
  681. }
  682.  
  683. /**
  684.  * @brief System Clock Configuration
  685.  * @retval None
  686.  */
  687. void SystemClock_Config(void)
  688. {
  689.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  690.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  691.  
  692.   /** Configure the main internal regulator output voltage
  693.    */
  694.   __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  695.  
  696.   /** Initializes the RCC Oscillators according to the specified parameters
  697.    * in the RCC_OscInitTypeDef structure.
  698.    */
  699.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  700.   RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  701.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  702.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  703.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
  704.   RCC_OscInitStruct.PLL.PLLDIV = RCC_PLL_DIV3;
  705.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  706.   {
  707.     Error_Handler();
  708.   }
  709.  
  710.   /** Initializes the CPU, AHB and APB buses clocks
  711.    */
  712.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  713.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  714.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  715.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  716.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  717.  
  718.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
  719.   {
  720.     Error_Handler();
  721.   }
  722. }
  723.  
  724. /**
  725.  * @brief I2C1 Initialization Function
  726.  * @param None
  727.  * @retval None
  728.  */
  729. static void MX_I2C1_Init(void)
  730. {
  731.  
  732.   /* USER CODE BEGIN I2C1_Init 0 */
  733.  
  734.   /* USER CODE END I2C1_Init 0 */
  735.  
  736.   /* USER CODE BEGIN I2C1_Init 1 */
  737.  
  738.   /* USER CODE END I2C1_Init 1 */
  739.   hi2c1.Instance = I2C1;
  740.   hi2c1.Init.ClockSpeed = 100000;
  741.   hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  742.   hi2c1.Init.OwnAddress1 = 0;
  743.   hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  744.   hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  745.   hi2c1.Init.OwnAddress2 = 0;
  746.   hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  747.   hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  748.   if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  749.   {
  750.     Error_Handler();
  751.   }
  752.   /* USER CODE BEGIN I2C1_Init 2 */
  753.  
  754.   /* USER CODE END I2C1_Init 2 */
  755. }
  756.  
  757. /**
  758.  * @brief SPI1 Initialization Function
  759.  * @param None
  760.  * @retval None
  761.  */
  762. static void MX_SPI1_Init(void)
  763. {
  764.  
  765.   /* USER CODE BEGIN SPI1_Init 0 */
  766.  
  767.   /* USER CODE END SPI1_Init 0 */
  768.  
  769.   /* USER CODE BEGIN SPI1_Init 1 */
  770.  
  771.   /* USER CODE END SPI1_Init 1 */
  772.   /* SPI1 parameter configuration*/
  773.   hspi1.Instance = SPI1;
  774.   hspi1.Init.Mode = SPI_MODE_MASTER;
  775.   hspi1.Init.Direction = SPI_DIRECTION_1LINE;
  776.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  777.   hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
  778.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  779.   hspi1.Init.NSS = SPI_NSS_SOFT;
  780.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  781.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  782.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  783.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  784.   hspi1.Init.CRCPolynomial = 10;
  785.   if (HAL_SPI_Init(&hspi1) != HAL_OK)
  786.   {
  787.     Error_Handler();
  788.   }
  789.   /* USER CODE BEGIN SPI1_Init 2 */
  790.  
  791.   /* USER CODE END SPI1_Init 2 */
  792. }
  793.  
  794. /**
  795.  * @brief TIM2 Initialization Function
  796.  * @param None
  797.  * @retval None
  798.  */
  799. static void MX_TIM2_Init(void)
  800. {
  801.  
  802.   /* USER CODE BEGIN TIM2_Init 0 */
  803.  
  804.   /* USER CODE END TIM2_Init 0 */
  805.  
  806.   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  807.   TIM_MasterConfigTypeDef sMasterConfig = {0};
  808.  
  809.   /* USER CODE BEGIN TIM2_Init 1 */
  810.  
  811.   /* USER CODE END TIM2_Init 1 */
  812.   htim2.Instance = TIM2;
  813.   htim2.Init.Prescaler = 0;
  814.   htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  815.   htim2.Init.Period = 65535;
  816.   htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  817.   htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  818.   if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  819.   {
  820.     Error_Handler();
  821.   }
  822.   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  823.   if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  824.   {
  825.     Error_Handler();
  826.   }
  827.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  828.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  829.   if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  830.   {
  831.     Error_Handler();
  832.   }
  833.   /* USER CODE BEGIN TIM2_Init 2 */
  834.  
  835.   /* USER CODE END TIM2_Init 2 */
  836. }
  837.  
  838. /**
  839.  * @brief TIM3 Initialization Function
  840.  * @param None
  841.  * @retval None
  842.  */
  843. static void MX_TIM3_Init(void)
  844. {
  845.  
  846.   /* USER CODE BEGIN TIM3_Init 0 */
  847.  
  848.   /* USER CODE END TIM3_Init 0 */
  849.  
  850.   TIM_Encoder_InitTypeDef sConfig = {0};
  851.   TIM_MasterConfigTypeDef sMasterConfig = {0};
  852.  
  853.   /* USER CODE BEGIN TIM3_Init 1 */
  854.  
  855.   /* USER CODE END TIM3_Init 1 */
  856.   htim3.Instance = TIM3;
  857.   htim3.Init.Prescaler = 0;
  858.   htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  859.   htim3.Init.Period = 65535;
  860.   htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  861.   htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  862.   sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
  863.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  864.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  865.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  866.   sConfig.IC1Filter = 15;
  867.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  868.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  869.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  870.   sConfig.IC2Filter = 15;
  871.   if (HAL_TIM_Encoder_Init(&htim3, &sConfig) != HAL_OK)
  872.   {
  873.     Error_Handler();
  874.   }
  875.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  876.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  877.   if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  878.   {
  879.     Error_Handler();
  880.   }
  881.   /* USER CODE BEGIN TIM3_Init 2 */
  882.  
  883.   /* USER CODE END TIM3_Init 2 */
  884. }
  885.  
  886. /**
  887.  * @brief TIM9 Initialization Function
  888.  * @param None
  889.  * @retval None
  890.  */
  891. static void MX_TIM9_Init(void)
  892. {
  893.  
  894.   /* USER CODE BEGIN TIM9_Init 0 */
  895.  
  896.   /* USER CODE END TIM9_Init 0 */
  897.  
  898.   TIM_Encoder_InitTypeDef sConfig = {0};
  899.   TIM_MasterConfigTypeDef sMasterConfig = {0};
  900.  
  901.   /* USER CODE BEGIN TIM9_Init 1 */
  902.  
  903.   /* USER CODE END TIM9_Init 1 */
  904.   htim9.Instance = TIM9;
  905.   htim9.Init.Prescaler = 0;
  906.   htim9.Init.CounterMode = TIM_COUNTERMODE_UP;
  907.   htim9.Init.Period = 65535;
  908.   htim9.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  909.   htim9.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  910.   sConfig.EncoderMode = TIM_ENCODERMODE_TI1;
  911.   sConfig.IC1Polarity = TIM_ICPOLARITY_RISING;
  912.   sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  913.   sConfig.IC1Prescaler = TIM_ICPSC_DIV1;
  914.   sConfig.IC1Filter = 15;
  915.   sConfig.IC2Polarity = TIM_ICPOLARITY_RISING;
  916.   sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  917.   sConfig.IC2Prescaler = TIM_ICPSC_DIV1;
  918.   sConfig.IC2Filter = 0;
  919.   if (HAL_TIM_Encoder_Init(&htim9, &sConfig) != HAL_OK)
  920.   {
  921.     Error_Handler();
  922.   }
  923.   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  924.   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  925.   if (HAL_TIMEx_MasterConfigSynchronization(&htim9, &sMasterConfig) != HAL_OK)
  926.   {
  927.     Error_Handler();
  928.   }
  929.   /* USER CODE BEGIN TIM9_Init 2 */
  930.  
  931.   /* USER CODE END TIM9_Init 2 */
  932. }
  933.  
  934. /**
  935.  * @brief UART4 Initialization Function
  936.  * @param None
  937.  * @retval None
  938.  */
  939. static void MX_UART4_Init(void)
  940. {
  941.  
  942.   /* USER CODE BEGIN UART4_Init 0 */
  943.  
  944.   /* USER CODE END UART4_Init 0 */
  945.  
  946.   /* USER CODE BEGIN UART4_Init 1 */
  947.  
  948.   /* USER CODE END UART4_Init 1 */
  949.   huart4.Instance = UART4;
  950.   huart4.Init.BaudRate = 4800;
  951.   huart4.Init.WordLength = UART_WORDLENGTH_8B;
  952.   huart4.Init.StopBits = UART_STOPBITS_1;
  953.   huart4.Init.Parity = UART_PARITY_NONE;
  954.   huart4.Init.Mode = UART_MODE_TX_RX;
  955.   huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  956.   huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  957.   if (HAL_UART_Init(&huart4) != HAL_OK)
  958.   {
  959.     Error_Handler();
  960.   }
  961.   /* USER CODE BEGIN UART4_Init 2 */
  962.  
  963.   /* USER CODE END UART4_Init 2 */
  964. }
  965.  
  966. /**
  967.  * @brief USART1 Initialization Function
  968.  * @param None
  969.  * @retval None
  970.  */
  971. static void MX_USART1_UART_Init(void)
  972. {
  973.  
  974.   /* USER CODE BEGIN USART1_Init 0 */
  975.  
  976.   /* USER CODE END USART1_Init 0 */
  977.  
  978.   /* USER CODE BEGIN USART1_Init 1 */
  979.  
  980.   /* USER CODE END USART1_Init 1 */
  981.   huart1.Instance = USART1;
  982.   huart1.Init.BaudRate = 19200;
  983.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
  984.   huart1.Init.StopBits = UART_STOPBITS_1;
  985.   huart1.Init.Parity = UART_PARITY_NONE;
  986.   huart1.Init.Mode = UART_MODE_TX_RX;
  987.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  988.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  989.   if (HAL_UART_Init(&huart1) != HAL_OK)
  990.   {
  991.     Error_Handler();
  992.   }
  993.   /* USER CODE BEGIN USART1_Init 2 */
  994.  
  995.   /* USER CODE END USART1_Init 2 */
  996. }
  997.  
  998. /**
  999.  * @brief USART2 Initialization Function
  1000.  * @param None
  1001.  * @retval None
  1002.  */
  1003. static void MX_USART2_UART_Init(void)
  1004. {
  1005.  
  1006.   /* USER CODE BEGIN USART2_Init 0 */
  1007.  
  1008.   /* USER CODE END USART2_Init 0 */
  1009.  
  1010.   /* USER CODE BEGIN USART2_Init 1 */
  1011.  
  1012.   /* USER CODE END USART2_Init 1 */
  1013.   huart2.Instance = USART2;
  1014.   huart2.Init.BaudRate = 115200;
  1015.   huart2.Init.WordLength = UART_WORDLENGTH_8B;
  1016.   huart2.Init.StopBits = UART_STOPBITS_1;
  1017.   huart2.Init.Parity = UART_PARITY_NONE;
  1018.   huart2.Init.Mode = UART_MODE_TX_RX;
  1019.   huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  1020.   huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  1021.   if (HAL_UART_Init(&huart2) != HAL_OK)
  1022.   {
  1023.     Error_Handler();
  1024.   }
  1025.   /* USER CODE BEGIN USART2_Init 2 */
  1026.  
  1027.   /* USER CODE END USART2_Init 2 */
  1028. }
  1029.  
  1030. /**
  1031.  * @brief USART3 Initialization Function
  1032.  * @param None
  1033.  * @retval None
  1034.  */
  1035. static void MX_USART3_UART_Init(void)
  1036. {
  1037.  
  1038.   /* USER CODE BEGIN USART3_Init 0 */
  1039.  
  1040.   /* USER CODE END USART3_Init 0 */
  1041.  
  1042.   /* USER CODE BEGIN USART3_Init 1 */
  1043.  
  1044.   /* USER CODE END USART3_Init 1 */
  1045.   huart3.Instance = USART3;
  1046.   huart3.Init.BaudRate = 19200;
  1047.   huart3.Init.WordLength = UART_WORDLENGTH_8B;
  1048.   huart3.Init.StopBits = UART_STOPBITS_1;
  1049.   huart3.Init.Parity = UART_PARITY_NONE;
  1050.   huart3.Init.Mode = UART_MODE_TX_RX;
  1051.   huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  1052.   huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  1053.   if (HAL_UART_Init(&huart3) != HAL_OK)
  1054.   {
  1055.     Error_Handler();
  1056.   }
  1057.   /* USER CODE BEGIN USART3_Init 2 */
  1058.  
  1059.   /* USER CODE END USART3_Init 2 */
  1060. }
  1061.  
  1062. /**
  1063.  * @brief GPIO Initialization Function
  1064.  * @param None
  1065.  * @retval None
  1066.  */
  1067. static void MX_GPIO_Init(void)
  1068. {
  1069.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  1070.  
  1071.   /* GPIO Ports Clock Enable */
  1072.   __HAL_RCC_GPIOH_CLK_ENABLE();
  1073.   __HAL_RCC_GPIOA_CLK_ENABLE();
  1074.   __HAL_RCC_GPIOC_CLK_ENABLE();
  1075.   __HAL_RCC_GPIOB_CLK_ENABLE();
  1076.  
  1077.   /*Configure GPIO pin Output Level */
  1078.   HAL_GPIO_WritePin(GPIOA, SPI_NSS1_Pin | BT_BUTTON_Pin | BT_RESET_Pin, GPIO_PIN_SET);
  1079.  
  1080.   /*Configure GPIO pin Output Level */
  1081.   HAL_GPIO_WritePin(SPI_CD_GPIO_Port, SPI_CD_Pin, GPIO_PIN_RESET);
  1082.  
  1083.   /*Configure GPIO pin Output Level */
  1084.   HAL_GPIO_WritePin(GPIOC, SPI_RESET_Pin | POWER_LATCH_Pin | USB_PWR_Pin, GPIO_PIN_RESET);
  1085.  
  1086.   /*Configure GPIO pin Output Level */
  1087.   HAL_GPIO_WritePin(SPI_NSS2_GPIO_Port, SPI_NSS2_Pin, GPIO_PIN_SET);
  1088.  
  1089.   /*Configure GPIO pins : SPI_NSS1_Pin SPI_CD_Pin */
  1090.   GPIO_InitStruct.Pin = SPI_NSS1_Pin | SPI_CD_Pin;
  1091.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  1092.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  1093.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  1094.   HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  1095.  
  1096.   /*Configure GPIO pins : SPI_RESET_Pin SPI_NSS2_Pin POWER_LATCH_Pin USB_PWR_Pin */
  1097.   GPIO_InitStruct.Pin = SPI_RESET_Pin | SPI_NSS2_Pin | POWER_LATCH_Pin | USB_PWR_Pin;
  1098.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  1099.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  1100.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  1101.   HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  1102.  
  1103.   /*Configure GPIO pins : BT_STATE_Pin SW1_PUSH_Pin SW2_PUSH_Pin */
  1104.   GPIO_InitStruct.Pin = BT_STATE_Pin | SW1_PUSH_Pin | SW2_PUSH_Pin;
  1105.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  1106.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  1107.   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  1108.  
  1109.   /*Configure GPIO pin : IGNITION_Pin */
  1110.   GPIO_InitStruct.Pin = IGNITION_Pin;
  1111.   GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  1112.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  1113.   HAL_GPIO_Init(IGNITION_GPIO_Port, &GPIO_InitStruct);
  1114.  
  1115.   /*Configure GPIO pin : BT_BUTTON_Pin */
  1116.   GPIO_InitStruct.Pin = BT_BUTTON_Pin;
  1117.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  1118.   GPIO_InitStruct.Pull = GPIO_PULLUP;
  1119.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  1120.   HAL_GPIO_Init(BT_BUTTON_GPIO_Port, &GPIO_InitStruct);
  1121.  
  1122.   /*Configure GPIO pin : BT_RESET_Pin */
  1123.   GPIO_InitStruct.Pin = BT_RESET_Pin;
  1124.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  1125.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  1126.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  1127.   HAL_GPIO_Init(BT_RESET_GPIO_Port, &GPIO_InitStruct);
  1128. }
  1129.  
  1130. /* USER CODE BEGIN 4 */
  1131.  
  1132. /* USER CODE END 4 */
  1133.  
  1134. /**
  1135.  * @brief  This function is executed in case of error occurrence.
  1136.  * @retval None
  1137.  */
  1138. void Error_Handler(void)
  1139. {
  1140.   /* USER CODE BEGIN Error_Handler_Debug */
  1141.   /* User can add his own implementation to report the HAL error return state */
  1142.  
  1143.   /* USER CODE END Error_Handler_Debug */
  1144. }
  1145.  
  1146. #ifdef USE_FULL_ASSERT
  1147. /**
  1148.  * @brief  Reports the name of the source file and the source line number
  1149.  *         where the assert_param error has occurred.
  1150.  * @param  file: pointer to the source file name
  1151.  * @param  line: assert_param error line source number
  1152.  * @retval None
  1153.  */
  1154. void assert_failed(uint8_t *file, uint32_t line)
  1155. {
  1156.   /* USER CODE BEGIN 6 */
  1157.   /* User can add his own implementation to report the file name and line number,
  1158.      tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  1159.   /* USER CODE END 6 */
  1160. }
  1161. #endif /* USE_FULL_ASSERT */
  1162.