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