1 // 2 // Copyright (c) 2010-2022 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Peripherals.Sensor; 11 using Antmicro.Renode.Peripherals.I2C; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Core.Structure.Registers; 15 using Antmicro.Renode.Utilities; 16 17 namespace Antmicro.Renode.Peripherals.Sensors 18 { 19 public class MAX77818 : II2CPeripheral, IProvidesRegisterCollection<WordRegisterCollection> 20 { MAX77818(IMachine machine)21 public MAX77818(IMachine machine) 22 { 23 RegistersCollection = new WordRegisterCollection(this); 24 DefineRegisters(); 25 } 26 Write(byte[] data)27 public void Write(byte[] data) 28 { 29 if(data.Length == 0) 30 { 31 this.Log(LogLevel.Warning, "Unexpected write with no data"); 32 return; 33 } 34 35 var offset = 0; 36 if(state == States.Read) 37 { 38 this.Log(LogLevel.Warning, "Trying to write while in read mode; ignoring"); 39 return; 40 } 41 else if(state == States.Idle) 42 { 43 registerAddress = (Registers)data[0]; 44 offset = 1; 45 state = data.Length == 1 ? States.Read : States.Write; 46 } 47 48 if(data.Length > offset) 49 { 50 foreach(var item in data.Skip(offset).Select((value, index) => new { index, value })) 51 { 52 RegistersCollection.WriteWithOffset((long)registerAddress.Value, item.index, item.value); 53 } 54 } 55 } 56 Read(int count)57 public byte[] Read(int count) 58 { 59 if(!registerAddress.HasValue) 60 { 61 this.Log(LogLevel.Error, "Trying to read without setting address"); 62 return new byte[] {}; 63 } 64 65 if(state != States.Read) 66 { 67 this.Log(LogLevel.Error, "Trying to read while in write mode"); 68 return new byte[] {}; 69 } 70 71 var result = new byte[count]; 72 for(var i = 0; i < count; ++i) 73 { 74 result[i] = RegistersCollection.ReadWithOffset((long)registerAddress.Value, i); 75 } 76 return result; 77 } 78 FinishTransmission()79 public void FinishTransmission() 80 { 81 registerAddress = null; 82 state = States.Idle; 83 } 84 Reset()85 public void Reset() 86 { 87 RegistersCollection.Reset(); 88 registerAddress = null; 89 state = States.Idle; 90 } 91 92 public WordRegisterCollection RegistersCollection { get; } 93 94 public decimal Temperature { get; set; } 95 public decimal CellVoltage { get; set; } 96 public decimal Current { get; set; } 97 98 public decimal DesignCapacity { get; set; } 99 public decimal FullCapacity { get; set; } 100 public decimal AvailableCapacity { get; set; } 101 public decimal ReportedCapacity { get; set; } 102 public decimal MixCapacity { get; set; } 103 104 public decimal ReportedStateOfCharge { get; set; } 105 public decimal Age { get; set; } 106 public decimal QResidual { get; set; } 107 public decimal Cycles { get; set; } 108 ConvertTemperature(decimal temperature)109 private ushort ConvertTemperature(decimal temperature) 110 { 111 temperature = temperature.Clamp(MinTemperature, MaxTemperature); 112 return (ushort)((short)(temperature * 256.0m)); 113 } 114 DefineRegisters()115 private void DefineRegisters() 116 { 117 Registers.ReportedStateOfCharge.Define(this) 118 .WithValueField(0, 16, FieldMode.Read, name: "RepSOC", 119 valueProviderCallback: _ => (ushort)(ReportedStateOfCharge * 256.0m)) 120 ; 121 122 Registers.Age.Define(this) 123 .WithValueField(0, 16, FieldMode.Read, name: "AGE", 124 valueProviderCallback: _ => (ushort)(Age * 256.0m)) 125 ; 126 127 Registers.Temperature.Define(this) 128 .WithValueField(0, 16, FieldMode.Read, name: "TEMP", 129 valueProviderCallback: _ => ConvertTemperature(Temperature)) 130 ; 131 132 Registers.CellVoltage.Define(this) 133 .WithValueField(0, 16, FieldMode.Read, name: "VCELL", 134 valueProviderCallback: _ => (ushort)(CellVoltage / CellVoltageSensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 135 ; 136 137 Registers.Current.Define(this) 138 .WithValueField(0, 16, FieldMode.Read, name: "CURRENT", 139 valueProviderCallback: _ => (ushort)(Current / CurrentSensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 140 ; 141 142 Registers.AverageCurrent.Define(this) 143 .WithValueField(0, 16, FieldMode.Read, name: "AVGCURRENT", 144 valueProviderCallback: _ => (ushort)(Current / CurrentSensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 145 ; 146 147 Registers.Qresidual.Define(this) 148 .WithValueField(0, 16, FieldMode.Read, name: "Qresidual", 149 valueProviderCallback: _ => (ushort)(QResidual / CapacitySensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 150 ; 151 152 Registers.MixCapacity.Define(this) 153 .WithValueField(0, 16, FieldMode.Read, name: "MaxCap", 154 valueProviderCallback: _ => (ushort)(MixCapacity / CapacitySensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 155 ; 156 157 Registers.FullCapacity.Define(this) 158 .WithValueField(0, 16, FieldMode.Read, name: "FullCAP", 159 valueProviderCallback: _ => (ushort)(FullCapacity / CapacitySensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 160 ; 161 162 Registers.AverageTemperature.Define(this) 163 .WithValueField(0, 16, FieldMode.Read, name: "AVGTA", 164 valueProviderCallback: _ => ConvertTemperature(Temperature)) 165 ; 166 167 Registers.Cycles.Define(this) 168 .WithValueField(0, 16, FieldMode.Read, name: "CYCLES", 169 valueProviderCallback: _ => (ushort)(Cycles * 100.0m)) 170 ; 171 172 Registers.DesignCapacity.Define(this) 173 .WithValueField(0, 16, FieldMode.Read, name: "DesignCAP", 174 valueProviderCallback: _ => (ushort)(DesignCapacity / CapacitySensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 175 ; 176 177 Registers.AverageCellVoltage.Define(this) 178 .WithValueField(0, 16, FieldMode.Read, name: "AVGVCELL", 179 valueProviderCallback: _ => (ushort)(CellVoltage / CellVoltageSensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 180 ; 181 182 Registers.AvailableCapacity.Define(this) 183 .WithValueField(0, 16, FieldMode.Read, name: "AvailableCAP", 184 valueProviderCallback: _ => (ushort)(AvailableCapacity / CapacitySensitivity).Clamp(0m, (decimal)ushort.MaxValue)) 185 ; 186 } 187 188 private Registers? registerAddress; 189 private States state; 190 191 private const decimal MinTemperature = (decimal)short.MinValue / 256.0m; 192 private const decimal MaxTemperature = (decimal)short.MaxValue / 256.0m; 193 private const decimal CellVoltageSensitivity = 78.125e-06m; 194 private const decimal CurrentSensitivity = 0.078125m; 195 private const decimal CapacitySensitivity = 0.5m; 196 197 private enum States 198 { 199 Idle, 200 Write, 201 Read, 202 } 203 204 private enum Registers : byte 205 { 206 Status = 0x00, 207 VoltageAlarmThreshold = 0x01, 208 TemperatureAlarmThreshold = 0x02, 209 StateOfChargeAlarmThreshold = 0x03, 210 AtRate = 0x04, 211 ReportedCapacity = 0x05, 212 ReportedStateOfCharge = 0x06, 213 Age = 0x07, 214 Temperature = 0x08, 215 CellVoltage = 0x09, 216 Current = 0x0A, 217 AverageCurrent = 0x0B, 218 Qresidual = 0x0C, 219 MixSOC = 0x0D, 220 AvailableStateOfCharge = 0x0E, 221 MixCapacity = 0x0F, 222 223 FullCapacity = 0x10, 224 TimeToEmpty = 0x11, 225 QRtable00 = 0x12, 226 FullStateOfChargeThreshold = 0x13, 227 RSlow = 0x14, 228 // Reserved 229 AverageTemperature = 0x16, 230 Cycles = 0x17, 231 DesignCapacity = 0x18, 232 AverageCellVoltage = 0x19, 233 MaxMinTemperature = 0x1A, 234 MaxMinVoltage = 0x1B, 235 MaxMinCurrrent = 0x1C, 236 Config = 0x1D, 237 ICHGTerm = 0x1E, 238 AvailableCapacity = 0x1F, 239 240 TimeToFull = 0x20, 241 DevName = 0x21, 242 QRtable10 = 0x22, 243 FullCapacityNominal = 0x23, 244 TemperatureNominal = 0x24, 245 TemperatureLimit = 0x25, 246 // Reserved 247 AIn0 = 0x27, 248 LearnConfig = 0x28, 249 FilterConfig = 0x29, 250 RelaxConfig = 0x2A, 251 MiscConfig = 0x2B, 252 TemperatureGain = 0x2C, 253 TemperatureOffset = 0x2D, 254 CapacityGain = 0x2E, 255 CapacityOffset = 0x2F, 256 257 // Reserved 258 // Reserved 259 QRtable20 = 0x32, 260 AtTimeToFull = 0x33, 261 // Reserved 262 FullCapacityRep = 0x35, 263 AverageCurrentEmptyEvent = 0x36, 264 FCTC = 0x37, 265 RComp0 = 0x38, 266 TempCoefficient = 0x39, 267 VoltageEmpty = 0x3A, 268 // Reserved 269 // Reserved 270 // Reserved 271 Timer = 0x3E, 272 ShutdownTimer = 0x3F, 273 274 // Reserved 275 // Reserved 276 QRtable30 = 0x42, 277 // Reserved 278 // Reserved 279 dQAcc = 0x45, 280 dPAcc = 0x46, 281 // Reserved 282 // Reserved 283 ConvergeConfig = 0x49, 284 VoltageFuelRemainingCapacity = 0x4A, 285 // Reserved 286 // Reserved 287 QH = 0x4D, 288 // Reserved 289 // Reserved 290 291 // Reserved 0x50-0x7F 292 293 OCV0 = 0x80, 294 OCV1 = 0x81, 295 OCV2 = 0x82, 296 OCV3 = 0x83, 297 OCV4 = 0x84, 298 OCV5 = 0x85, 299 OCV6 = 0x86, 300 OCV7 = 0x87, 301 OCV8 = 0x88, 302 OCV9 = 0x89, 303 OCVA = 0x8A, 304 OCVB = 0x8B, 305 OCVC = 0x8C, 306 OCVD = 0x8D, 307 OCVE = 0x8E, 308 OCVF = 0x8F, 309 310 CapacityAvailableToApplication0 = 0x90, 311 CapacityAvailableToApplication1 = 0x91, 312 CapacityAvailableToApplication2 = 0x92, 313 CapacityAvailableToApplication3 = 0x93, 314 CapacityAvailableToApplication4 = 0x94, 315 CapacityAvailableToApplication5 = 0x95, 316 CapacityAvailableToApplication6 = 0x96, 317 CapacityAvailableToApplication7 = 0x97, 318 CapacityAvailableToApplication8 = 0x98, 319 CapacityAvailableToApplication9 = 0x99, 320 CapacityAvailableToApplicationA = 0x9A, 321 CapacityAvailableToApplicationB = 0x9B, 322 CapacityAvailableToApplicationC = 0x9C, 323 CapacityAvailableToApplicationD = 0x9D, 324 CapacityAvailableToApplicationE = 0x9E, 325 CapacityAvailableToApplicationF = 0x9F, 326 327 RCompSegment0 = 0xA0, 328 RCompSegment1 = 0xA1, 329 RCompSegment2 = 0xA2, 330 RCompSegment3 = 0xA3, 331 RCompSegment4 = 0xA4, 332 RCompSegment5 = 0xA5, 333 RCompSegment6 = 0xA6, 334 RCompSegment7 = 0xA7, 335 RCompSegment8 = 0xA8, 336 RCompSegment9 = 0xA9, 337 RCompSegmentA = 0xAA, 338 RCompSegmentB = 0xAB, 339 RCompSegmentC = 0xAC, 340 RCompSegmentD = 0xAD, 341 RCompSegmentE = 0xAE, 342 RCompSegmentF = 0xAF, 343 344 Status2 = 0xB0, 345 // Reserved 346 TemperatureAlarmThreshold2 = 0xB2, 347 // Reserved 348 // Reserved 349 TimeToFullConfig = 0xB5, 350 CVMixCapacity = 0xB6, 351 CVHalfTime = 0xB7, 352 CGTemperatureCoefficient = 0xB8, 353 Curve = 0xB9, 354 // Reserved 355 Config2 = 0xBB, 356 Vripple = 0xBC, 357 RippleConfig = 0xBD, 358 TimerH = 0xBE, 359 MaxError = 0xBF, 360 361 // Reserved 0xC0-0xCF 362 363 // Reserved 364 ChargeState0 = 0xD1, 365 ChargeState1 = 0xD2, 366 ChargeState2 = 0xD3, 367 ChargeState3 = 0xD4, 368 ChargeState4 = 0xD5, 369 ChargeState5 = 0xD6, 370 ChargeState6 = 0xD7, 371 ChargeState7 = 0xD8, 372 JEITAVoltage = 0xD9, 373 JEITACurrent = 0xDA, 374 SmartChargingConfig = 0xDB, 375 AtQresidual = 0xDC, 376 AtTimeToEmpty = 0xDD, 377 AtAvailableStateOfCharge = 0xDE, 378 AtAvailableCapacity = 0xDF, 379 380 // Reserved 0xE0-0xEF 381 382 // Reserved 0xF0-FA 383 VFOCV = 0xFB, 384 // Reserved 385 // Reserved 386 // Reserved 387 VFSOC = 0xFF, 388 } 389 } 390 } 391