Ignore:
Timestamp:
Sep 8, 2025, 6:57:30 PM (7 weeks ago)
Author:
f.jahn
Message:
  • Bug in ADC Kalibrierung (STM32 ADC Strom) behoben
  • DMA Buffer für ADC 1 und ADC wird vor Überschreibung während bearbeitung geschützt, indem Datenübertragung nur einmalig erfolgt und erst nach Auswertung wieder gestartet wird
  • RS485Modbus: Timeout Zeit wird für Baudraten >19200 korrekt berechnet
  • Hardware ID geändert
  • Separates Register für "Batterie Empty detection mode" auf Adresse 92 angelegt
Location:
trunk/fw_g473rct/SES/src
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/fw_g473rct/SES/src/ah_counter.c

    r20 r26  
    175175  sys_data.s.values.mAhCounter = sys_data.s.values.mAsCounter / 3600LL;
    176176
    177   static uint16_t lowVoltageCnt;
    178   if (sys_data.s.values.batteryVoltage < sys_data.s.values.uBatEmptyTempComp && sys_data.s.values.batteryVoltage > 1000)
    179   {
    180           lowVoltageCnt++;
    181           if ((lowVoltageCnt >= 10) && (startMeasurement == 1)) // 5 Sekunden fest
    182           {
    183                   lowVoltageCnt = 10; //sys_data.s.parameter.tBatFull;
    184 
    185                   if ((sys_data.s.values.lastTimeVbatFull >= 3600U) && (sys_data.s.values.lastTimeVbatFull <= 200U * 3600U))    // This line prevents from very high discharge-currents to be used to estimate battery capacity
    186                   {
    187                           // This line is not so important anymore, because we do not allow mAh_AutoMode to be greater than zero
    188                           sys_data.s.values.detectedCapacity = sys_data.s.values.mAh_AutoMode >= 0 ? sys_data.s.values.mAh_AutoMode : -sys_data.s.values.mAh_AutoMode;
    189                           WH_COUNTER_SetDetectedEnergy();
    190                           startMeasurement = 0;
    191                           EEPROM_storeConfig(&sys_data, 0);     // Saving detected values
    192                   }
    193                   sys_data.s.values.lastTimeVbatEmpty = 0U;
    194           }
    195   }
    196   else lowVoltageCnt = 0;
    197 
     177  static uint16_t cnt;
     178  if (sys_data.s.parameter.batteryEmptyDetectionMode == 0)
     179  {
     180    if (sys_data.s.values.batteryVoltage < sys_data.s.values.uBatEmptyTempComp && sys_data.s.values.batteryVoltage > 1000) // Verhindert das beim abziehen der Sense ein Batt Empty erkannt wird
     181    {
     182          cnt++;
     183          if ((cnt >= 10) && (startMeasurement == 1)) // 5 Sekunden fest
     184          {
     185                  cnt = 10; //sys_data.s.parameter.tBatFull;
     186
     187                  if ((sys_data.s.values.lastTimeVbatFull >= 3600U) && (sys_data.s.values.lastTimeVbatFull <= 200U * 3600U))    // This line prevents from very high discharge-currents to be used to estimate battery capacity
     188                  {
     189                          // This line is not so important anymore, because we do not allow mAh_AutoMode to be greater than zero
     190                          sys_data.s.values.detectedCapacity = sys_data.s.values.mAh_AutoMode >= 0 ? sys_data.s.values.mAh_AutoMode : -sys_data.s.values.mAh_AutoMode;
     191                          WH_COUNTER_SetDetectedEnergy();
     192                          startMeasurement = 0;                   
     193                  }
     194                  sys_data.s.values.lastTimeVbatEmpty = 0U;
     195          }
     196    }
     197    else
     198    {
     199      cnt = 0;
     200    }
     201  }
     202  else
     203  {
     204        // Neuer Modus. Spannungsmessung wird ignoriert. Erkannt wird Batt Leer mit LVP Signal von LiPro
     205        // OVP darf nicht ausgehen, sonst handelt es sich um ein Temperaturabschaltung oder ein andere Fehler
     206        // 1000mV als Schwelle um sicher vor rauschen um den Nullpunkt zu seinzu sein
     207    if ((sys_data.s.values.ovp_sense > 1000) && (sys_data.s.values.lvp_sense < 1000))
     208    {
     209          cnt++;
     210          if ((cnt >= 10) && (startMeasurement == 1)) // 5 Sekunden fest
     211          {
     212                  cnt = 10; //sys_data.s.parameter.tBatFull;
     213
     214                  if ((sys_data.s.values.lastTimeVbatFull >= 3600U) && (sys_data.s.values.lastTimeVbatFull <= 240U * 3600U))    // This line prevents from very high discharge-currents to be used to estimate battery capacity
     215                  {
     216                          // This line is not so important anymore, because we do not allow mAh_AutoMode to be greater than zero
     217                          sys_data.s.values.detectedCapacity = sys_data.s.values.mAh_AutoMode >= 0 ? sys_data.s.values.mAh_AutoMode : -sys_data.s.values.mAh_AutoMode;
     218                          WH_COUNTER_SetDetectedEnergy();
     219                          startMeasurement = 0;                   
     220                  }
     221                  sys_data.s.values.lastTimeVbatEmpty = 0U;
     222          }
     223    }
     224    else
     225    {
     226      cnt = 0;
     227    }
     228  }
    198229
    199230
  • trunk/fw_g473rct/SES/src/eeprom.c

    r25 r26  
    44#include "stdio.h"
    55#include "math.h"
     6#include "modbus.h"
    67
    78#define CONCAT(a, b) CONCAT_INNER(a, b)                                                                                 // These three macros
     
    5758  uint32_t        currentGain;
    5859
     60  int32_t         currentOffsetFast;
     61  uint32_t        currentGainFast;
     62
    5963  int64_t         mAsCounter;
    6064  int32_t         detectedCapacity;
     
    97101  uint16_t        cefW;
    98102
    99 
    100103} eeprom_data_t;
    101104
     
    136139
    137140// Data to store reated defines
    138 //#define SIZEOF_DEFAULT_EEPROM_DATA                (sizeof(eeprom_new_data_t))
     141//#define SIZEOF_DEFAULT_EEPROM_DATA              (sizeof(eeprom_new_data_t))
    139142#define SIZEOF_CHANGED_EEPROM_DATA                (sizeof(eeprom_data_t))
    140143#define SIZEOF_DEVICE_INFO                        (sizeof(device_info_t))
     
    156159static uint32_t GetPage(uint32_t Address);
    157160static HAL_StatusTypeDef getEEPROMData(uint32_t address, uint8_t * data, uint32_t len);
    158 
    159161void EEPROM_Read (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size);
    160162void EEPROM_Write (uint16_t page, uint16_t offset, uint8_t *data, uint16_t size);
     
    162164
    163165
    164 
    165166// muss modulo 8 noch hinzufügen wg 8 byte alignement
    166167static uint8_t eepromData[SIZE_OF_DATA_TO_STORE];
     
    174175
    175176        /* baudrate             */                                                                                                              19200,                  // uint32_t   baudrate;
    176         /* parityMode   */                                                                                                              0,                              // uint16_t   parityMode;
     177        /* parityMode   */                                                                                                              MODBUS_UART_PARITY_EVEN,  // uint16_t   parityMode;
    177178        /* stopBits             */                                                                                                              1,                              // uint16_t   stopBits;
    178179        /* slave_adress */                                                                                                              1,                              // uint16_t   slave_adress;
     
    202203        /* currentGain    */                                                                                                    1000000,                //uint32_t  currentGain;
    203204
     205        /* currentOffsetFast  */                                                                                                0,                              //int32_t   currentOffset;
     206        /* currentGainFast  */                                                                                                  1000000,                //uint32_t  currentGain;
     207
    204208        /* mAsCounter           */                                                                                                      0,                              // mAsCounter
    205209        /* detectedCapacity     */                                                                                                      -1,                             // detectedCapacity
     
    212216        /* cef                                          */                                                                                      99,                             // cef
    213217        /* peukert                                      */                                                                                      105,                    // peukert
    214         /* cellCapacity                         */                                                                                      100000,                 // cell Capacity in mAh
    215         /* cellEnergy                           */                                                                                      2640000,                // cell energy in mWh
    216         /* iBatFull                                     */                                                                                      10,                             // I-batt full 4% 4A bei 100Ah akku
     218        /* cellCapacity                         */                                                                                      160000,                 // cell Capacity in mAh
     219        /* cellEnergy                           */                                                                                      2048000,                // cell energy in mWh
     220        /* iBatFull                                     */                                                                                      10,                             // I-batt full 10%, 10A bei 100Ah akku
    217221        /* tBatFull                                     */                                                                                      2,                              // t-batt full 2 Sekunden
    218         /* uBatFull                                     */                                                                                      14000,                  // 14Volt Ubatt full
    219         /* uBatEmpty                            */                                                                                      12500,                  // 11,312V Ubatt Empty
     222        /* uBatFull                                     */                                                                                      0,                              // 14V olt Ubatt full, Neu: Bei 0: Erkung von Lipro LVP als 0%
     223        /* uBatEmpty                            */                                                                                      -1,                             // 11,312V Ubatt Empty
    220224        /* socCalcMode                          */                                                                                      1,                              // SoC calculation mode: 0(default)
    221225        /* cellRatedDischargeTime       */                                                                                      2,                              // cell rated current discharge time [C/x]. For example, if 40Ah cell is rated as 0.5c, then rated discharge time is 2
     
    223227        /* lvpStart     */                                                                                                                      12000,                  // uint16_t lvpStart; Spannung ab der die LOW Voltage Protection aktiv wird in mV
    224228        /* lvpStop      */                                                                                                                      12500,                  // uint16_t lvpStop; Spannung ab der die LOW Voltage Protection wieder inaktiv wird
    225         /* ovpStart     */                                                                                                                      15000,                  // uint16_t  ovpStart; Spannung ab der die OVER Voltage Protection aktiv wird in mV
     229        /* ovpStart     */                                                                                                                      14800,                  // uint16_t  ovpStart; Spannung ab der die OVER Voltage Protection aktiv wird in mV
    226230        /* ovpStop      */                                                                                                                      14000,                  // uint16_t  ovpStop; Spannung ab der die OVER Voltage Protection wieder inaktiv wird
    227231
     
    238242#error No valid device type
    239243#endif
    240         /* chargeStopHighTemperatureStart       */                                                                      7500,                   // 80°C int16_t chargeStopHighTemperatureStart; Abschalttemperatur Ladung wegen zu hoher Temperatur
    241         /* chargeStopHighTemperatureStop        */                                                                      7000,                   // 75°C int16_t chargeStopHighTemperatureStop;  Wiedereinschalttemperatur
    242         /* chargeStopLowTemperatureStart        */                                                                      -3500,                  // -35°C int16_t chargeStopLowTemperatureStart; Abschalttemperatur Ladung wegen zu niedriger Temperatur
    243         /* chargeStopLowTemperatureStop         */                                                                      -3000,                  // -30°C int16_t chargeStopLowTemperatureStop; Wiedereinschalttemperatur
    244         /* dischargeStopHighTemperatureStart*/                                                                  7500,                   // 80°C int16_t dischargeStopHighTemperatureStart; Abschalttemperatur Entladung wegen zu hoher Temperatur
    245         /* dischargeStopHighTemperatureStop     */                                                                      7000,                   // 75°C int16_t dischargeStopHighTemperatureStop; Wiedereinschalttemperatur
     244        /* chargeStopHighTemperatureStart       */                                                                      6000,                   // 80°C int16_t chargeStopHighTemperatureStart; Abschalttemperatur Ladung wegen zu hoher Temperatur
     245        /* chargeStopHighTemperatureStop        */                                                                      5500,                   // 75°C int16_t chargeStopHighTemperatureStop;  Wiedereinschalttemperatur
     246        /* chargeStopLowTemperatureStart        */                                                                      -3000,                  // -35°C int16_t chargeStopLowTemperatureStart; Abschalttemperatur Ladung wegen zu niedriger Temperatur
     247        /* chargeStopLowTemperatureStop         */                                                                      -2500,                  // -30°C int16_t chargeStopLowTemperatureStop; Wiedereinschalttemperatur
     248        /* dischargeStopHighTemperatureStart*/                                                                  6000,                   // 80°C int16_t dischargeStopHighTemperatureStart; Abschalttemperatur Entladung wegen zu hoher Temperatur
     249        /* dischargeStopHighTemperatureStop     */                                                                      5500,                   // 75°C int16_t dischargeStopHighTemperatureStop; Wiedereinschalttemperatur
    246250        /* dischargeStopLowTemperatureStart     */                                                                      -3500,                  // -35°C int16_t dischargeStopLowTemperatureStart; Abschalttemperatur EntLadung wegen zu niedriger Temperatur
    247251        /* dischargeStopLowTemperatureStop      */                                                                      -3000,                  // -30°C int16_t dischargeStopLowTemperatureStop; Wiedereinschalttemperatur
     
    330334  dataToStore->changedData.currentGain                                                                                          = defaultEepromData.currentGain;
    331335
     336  dataToStore->changedData.currentOffsetFast                                                                            = defaultEepromData.currentOffsetFast;
     337  dataToStore->changedData.currentGainFast                                                                                      = defaultEepromData.currentGainFast;
     338
    332339  // AH Counter Parameter
    333340  dataToStore->changedData.cef                                                                                                          = defaultEepromData.cef;
     
    403410
    404411  // Offset und Gain
    405   dataToStore->changedData.currentOffset                                                                                        = data->s.parameter.batteryCurrentOffset;
     412 
    406413  dataToStore->changedData.batteryCurrentOffsetRefTemperatureShunt                                      = data->s.parameter.batteryCurrentOffsetRefTemperatureShunt;
    407414  dataToStore->changedData.batteryCurrentOffsetRefTemperatureChip                                       = data->s.parameter.batteryCurrentOffsetRefTemperatureChip;
     
    411418  dataToStore->changedData.batteryCurrentOffsetTemperatureCalibrationTemperature        = data->s.parameter.batteryCurrentOffsetTemperatureCalibrationTemperature;
    412419  dataToStore->changedData.batteryCurrentOffsetTemperatureCompensationFactor            = data->s.parameter.batteryCurrentOffsetTemperatureCompensationFactor;
     420  dataToStore->changedData.currentOffset                                                                                        = data->s.parameter.batteryCurrentOffset;
    413421  dataToStore->changedData.currentGain                                                                                          = data->s.parameter.batteryCurrentGainCorrectionFaktor;
     422  dataToStore->changedData.currentOffsetFast                                                                            = data->s.parameter.batteryCurrentOffsetFast;
     423  dataToStore->changedData.currentGainFast                                                                                      = data->s.parameter.batteryCurrentGainCorrectionFaktorFast;
     424 
    414425  dataToStore->changedData.batteryCurrentGainRefTempShunt                                                       = data->s.parameter.batteryCurrentGainRefTempShunt;
    415426  dataToStore->changedData.batteryCurrentGainRefTempChip                                                        = data->s.parameter.batteryCurrentGainRefTempChip;
     
    520531  dataToStore->changedData.currentOffset = data->s.parameter.batteryCurrentOffset;
    521532  dataToStore->changedData.currentGain = data->s.parameter.batteryCurrentGainCorrectionFaktor;
     533
     534  dataToStore->changedData.currentOffsetFast = data->s.parameter.batteryCurrentOffsetFast;
     535  dataToStore->changedData.currentGainFast = data->s.parameter.batteryCurrentGainCorrectionFaktorFast;
     536
    522537
    523538  // AH COUNTER Einstellungen
     
    616631  data->s.parameter.batteryCurrentGainCorrectionFaktor                                                  = dataToStore->changedData.currentGain;
    617632
     633  data->s.parameter.batteryCurrentOffsetFast                                                                    = dataToStore->changedData.currentOffsetFast;
     634  data->s.parameter.batteryCurrentGainCorrectionFaktorFast                                              = dataToStore->changedData.currentGainFast;
     635
    618636  //Einstellungenm für AH counter
    619637  data->s.parameter.cef                                                                                                                 = dataToStore ->changedData.cef;
  • trunk/fw_g473rct/SES/src/esr.c

    r25 r26  
    104104  {
    105105    //ESR berechnen!
    106     sys_data.s.values.esr = ( (double)dU / (double) dI) * 1000;
     106    sys_data.s.values.esr = ( (double)dU / (double) dI) * 10000;
    107107    last_refresh = sys_data.s.values.onTime;
    108108
     
    129129
    130130  //Anzeige vor wieviel Sekunden zuletzt aktualisiert wurd.
    131   sys_data.s.values.esrCalcTime = sys_data.s.values.onTime - last_refresh;
     131  //Aktuell erfolgt nur die Anze der low speed Methode
     132  //sys_data.s.values.esrCalcTime = sys_data.s.values.onTime - last_refresh;
    132133
    133134
     
    153154
    154155 
    155   //Suche Zeitpunkt der größten Änderung in I
     156
    156157
    157158  //Delta berechnen
    158159  int32_t dI = maxI - minI;
    159   int32_t dU = maxU - minU;
     160 
     161  //Nehme nicht mehr die gesamte maximale Differenz der Spannungen, sondern nehme das delt U wo auch das Delta I gemessen wurde
     162  //Funktioniert nur bei Synchroner Messug von Strom und Spannung
     163  //int32_t dU = maxU - minU;
     164  int32_t dU = adc12Data[maxdIPos][1]  - adc12Data[minIPos][1];
    160165
    161166  //Umrechnung in mV / mA
    162   dI = dI * ((int64_t) VREF / FAST_CURRENT_SHUNT_RESISTOR /  FAST_CURRENT_I_SENSE_GAIN /  FAST_CURRENT_ADC_RESOLUTION);
     167  dI = dI * ((double) VREF / FAST_CURRENT_SHUNT_RESISTOR /  FAST_CURRENT_I_SENSE_GAIN /  FAST_CURRENT_ADC_RESOLUTION);
    163168  dI = dI * (sys_data.s.parameter.batteryCurrentGainCorrectionFaktor / 1000000.0);
    164169
    165   dU = dU  * VREF * BATTERY_VOLTAGE_VOLTAGE_DIVIDER / BATTERY_VOLTAGE_ADC_RESOLUTION ;
     170  dU = dU  * (double )VREF * BATTERY_VOLTAGE_VOLTAGE_DIVIDER / BATTERY_VOLTAGE_ADC_RESOLUTION ;
    166171 
    167172
     
    204209  }
    205210
    206   if ((dIMaxPos < 5 ) || (dIMaxPos > (SAMPLE_ARRAY_SIZE-5) ))
    207   {
    208         return -3;
    209   }
     211  //if ((dIMaxPos < 5 ) || (dIMaxPos > (SAMPLE_ARRAY_SIZE-5) ))
     212  //{
     213//      return -3;
     214 // }
    210215
    211216
    212217  //ESR berechnen!
    213   sys_data.s.values.esr_fast = ( (double)dU / (double) dI) * 1000;
     218  sys_data.s.values.esr_fast = ( (double)dU / (double) dI) * 10000;
    214219  last_refresh = sys_data.s.values.onTime;
    215220       
  • trunk/fw_g473rct/SES/src/fast_current.c

    r25 r26  
    3232
    3333//      --- GLOBALE FUNKTIONEN - bitte in Header dokumentieren------------------------
     34void CurrentOffsetCal(uint32_t newVal)
     35{
     36  sys_data.s.parameter.batteryCurrentOffsetFast = newVal-FAST_CURRENT_ADC_OFFSET;
     37}
    3438
     39void CurrentGainCal(uint32_t newVal)
     40{
     41  double correction;
     42  double valWithoutGainCorrection;
    3543
     44  valWithoutGainCorrection = ((int32_t) newVal - FAST_CURRENT_ADC_OFFSET - sys_data.s.parameter.batteryCurrentOffsetFast) * VREF ;
     45  valWithoutGainCorrection = valWithoutGainCorrection / FAST_CURRENT_ADC_RESOLUTION;
     46  valWithoutGainCorrection = valWithoutGainCorrection / FAST_CURRENT_I_SENSE_GAIN ;
     47  valWithoutGainCorrection = valWithoutGainCorrection / FAST_CURRENT_SHUNT_RESISTOR ;
     48
     49  correction = (double)  sys_data.s.parameter.batteryCurrentGainRefCurrent / valWithoutGainCorrection;
     50  sys_data.s.parameter.batteryCurrentGainCorrectionFaktorFast = correction * 1000000;
     51
     52}
    3653void FAST_CURRENT_Exec(uint32_t newVal )
    3754{
     
    3956  //Umrechung auf Strom
    4057  double temp_current;
    41   temp_current = ((int32_t) newVal - FAST_CURRENT_ADC_OFFSET) * VREF ;
     58  temp_current = ((int32_t) newVal - FAST_CURRENT_ADC_OFFSET - sys_data.s.parameter.batteryCurrentOffsetFast) * VREF ;
    4259  temp_current = temp_current / FAST_CURRENT_ADC_RESOLUTION;
    4360  temp_current = temp_current / FAST_CURRENT_I_SENSE_GAIN ;
    4461  temp_current = temp_current / FAST_CURRENT_SHUNT_RESISTOR ;
    45   sys_data.s.values.fast_current = temp_current * (sys_data.s.parameter.batteryCurrentGainCorrectionFaktor / 1000000.0);
     62  sys_data.s.values.fast_current = temp_current * (sys_data.s.parameter.batteryCurrentGainCorrectionFaktorFast / 1000000.0);
    4663 
    4764}
  • trunk/fw_g473rct/SES/src/modbus.c

    r24 r26  
    3434  #define ILLEGAL_DATA_VALUE              0x03
    3535  #define SLAVE_DEVICE_FAILURE            0x04
    36   #define SERVER_FAILURE                    0x04
     36  #define SERVER_FAILURE                  0x04
    3737  #define ACKNOWLEDGE                     0x05
    3838  #define SLAVE_DEVICE_BUSY               0x06
     
    4141  #define MEMORY_PARITY_ERROR             0x08
    4242  #define GATEWAY_PROBLEM_PATH            0x0A
    43   #define GATEWAY_PROBLEM_TARGET            0x0B
     43  #define GATEWAY_PROBLEM_TARGET          0x0B
    4444
    4545  /* Local Error codes */
     
    185185       
    186186        HAL_UART_EnableReceiverTimeout( usart);
    187         HAL_UART_ReceiverTimeout_Config(usart,  3.5 * nrOfBitsPerChar);
     187        if (baudrate <= 19200)
     188        {
     189          HAL_UART_ReceiverTimeout_Config(usart,  3.5 * nrOfBitsPerChar);
     190        }
     191        else
     192        {
     193      uint32_t fixedDelayInBitDurations = (FAST_BAUDRATE_INTERFRAME_DELAY_us * baudrate) / 1000000UL + 1UL;
     194          HAL_UART_ReceiverTimeout_Config(usart, fixedDelayInBitDurations);
     195        }
     196
     197
    188198        SET_BIT(usart->Instance->CR1, USART_CR1_RTOIE);
    189199
  • trunk/fw_g473rct/SES/src/sysdata.c

    r25 r26  
    1313        // Gertetyp angeben
    1414#if (DEVICETYPE == 500)
    15         sys_data.s.parameter.device_type                        = 510;
     15        sys_data.s.parameter.device_type                        = 520;
    1616#elif (DEVICETYPE == 250)
    17         sys_data.s.parameter.device_type                        = 511;
     17        sys_data.s.parameter.device_type                        = 521;
    1818#elif (DEVICETYPE == 125)
    19         sys_data.s.parameter.device_type                        = 512;
     19        sys_data.s.parameter.device_type                        = 522;
    2020#else
    2121#error "No valid device type"
  • trunk/fw_g473rct/SES/src/wh_counter.c

    r20 r26  
    7878  sys_data.s.values.mWhCounter = sys_data.s.values.mWsCounter / 3600LL;
    7979
    80 
    81 
    82 
    8380  // Counting Total Power
    8481  if (sys_data.s.values.batteryPower < 0)
    8582  {
    8683          totalDischarge += -sys_data.s.values.batteryPower;
    87           sys_data.s.values.dischargeTotalWh = totalDischarge / 3600000; //Umrechnung von mWs auf Wh
    88 
    89 
    90           sys_data.s.values.fullCyclesCnt = (uint16_t) ((sys_data.s.values.dischargeTotalAh * 1000) / sys_data.s.parameter.cellCapacity);
     84          sys_data.s.values.dischargeTotalWh = totalDischarge / 3600000; //Umrechnung von mWs auf Wh   
    9185  }
    9286  else
     
    9589          sys_data.s.values.chargeTotalWh = totalCharge / 3600000; //Umrechnung von mWs auf Wh
    9690  }
    97 
    98 
    99 
    10091}
    10192
Note: See TracChangeset for help on using the changeset viewer.