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 TMP103 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ITemperatureSensor
20     {
TMP103(IMachine machine)21         public TMP103(IMachine machine)
22         {
23             RegistersCollection = new ByteRegisterCollection(this);
24             DefineRegisters();
25 
26             Reset();
27         }
28 
Write(byte[] data)29         public void Write(byte[] data)
30         {
31             if(data.Length == 0)
32             {
33                 this.Log(LogLevel.Warning, "Unexpected write with no data");
34                 return;
35             }
36 
37             registerAddress = (Registers)data[0];
38 
39             if(data.Length > 1)
40             {
41                 foreach(var value in data.Skip(1))
42                 {
43                     RegistersCollection.Write((byte)registerAddress, value);
44                 }
45             }
46         }
47 
Read(int count)48         public byte[] Read(int count)
49         {
50             if(!registerAddress.HasValue)
51             {
52                 this.Log(LogLevel.Error, "Trying to read without setting address");
53                 return new byte[] {};
54             }
55 
56             var result = new byte[count];
57             for(var i = 0; i < count; ++i)
58             {
59                 result[i] = RegistersCollection.Read((byte)((int)registerAddress + i));
60             }
61             return result;
62         }
63 
FinishTransmission()64         public void FinishTransmission()
65         {
66             registerAddress = null;
67         }
68 
Reset()69         public void Reset()
70         {
71             RegistersCollection.Reset();
72             registerAddress = null;
73 
74             currentTemperature = 0;
75             temperatureLowThreshold = defaultLowThreshold;
76             temperatureHighThreshold = defaultHighThreshold;
77         }
78 
79         public ByteRegisterCollection RegistersCollection { get; }
80 
81         public decimal Temperature
82         {
83             get => (decimal)currentTemperature;
84             set
85             {
86                 if(value > sbyte.MaxValue)
87                 {
88                     currentTemperature = sbyte.MaxValue;
89                     this.Log(LogLevel.Warning, "{0} is higher than maximum of {1} and has been clamped", value, sbyte.MaxValue);
90                 }
91                 else if(value < sbyte.MinValue)
92                 {
93                     currentTemperature = sbyte.MinValue;
94                     this.Log(LogLevel.Warning, "{0} is lower than minimum of {1} and has been clamped", value, sbyte.MinValue);
95                 }
96                 else
97                 {
98                     currentTemperature = (sbyte)value;
99                 }
100 
101                 UpdateThresholdFlags();
102             }
103         }
104 
UpdateThresholdFlags()105         private void UpdateThresholdFlags()
106         {
107             if(latchFlags.Value)
108             {
109                 temperatureLowFlag.Value |= currentTemperature < temperatureLowThreshold;
110                 temperatureHighFlag.Value |= currentTemperature > temperatureHighThreshold;
111             }
112             else
113             {
114                 temperatureLowFlag.Value = currentTemperature < temperatureLowThreshold;
115                 temperatureHighFlag.Value = currentTemperature > temperatureHighThreshold;
116             }
117         }
118 
DefineRegisters()119         private void DefineRegisters()
120         {
121             Registers.Temperature.Define(this)
122                 .WithValueField(0, 8, name: "TEMP.temp",
123                     valueProviderCallback: _ => (uint)currentTemperature,
124                     writeCallback: (_, value) => currentTemperature = (sbyte)value)
125             ;
126 
127             Registers.Configuration.Define(this, 0x2)
128                 .WithTag("CONF.mode", 0, 2)
129                 .WithFlag(2, out latchFlags, name: "CONF.latch")
130                 .WithFlag(3, out temperatureLowFlag, name: "CONF.fl")
131                 .WithFlag(4, out temperatureHighFlag, name: "CONF.fh")
132                 .WithTag("CONF.conv_rate", 5, 2)
133                 .WithTaggedFlag("CONF.id", 7)
134             ;
135 
136             Registers.TemperatureLow.Define(this)
137                 .WithValueField(0, 8, name: "TLOW.tlow",
138                     valueProviderCallback: _ => (uint)temperatureLowThreshold,
139                     writeCallback: (_, value) =>
140                     {
141                         temperatureLowThreshold = (sbyte)value;
142                         UpdateThresholdFlags();
143                     })
144             ;
145 
146             Registers.TemperatureHigh.Define(this)
147                 .WithValueField(0, 8, name: "THIGH.thigh",
148                     valueProviderCallback: _ => (uint)temperatureHighThreshold,
149                     writeCallback: (_, value) =>
150                     {
151                         temperatureHighThreshold = (sbyte)value;
152                         UpdateThresholdFlags();
153                     })
154             ;
155         }
156 
157         private Registers? registerAddress;
158 
159         private sbyte currentTemperature;
160         private sbyte temperatureLowThreshold;
161         private sbyte temperatureHighThreshold;
162 
163         private IFlagRegisterField latchFlags;
164         private IFlagRegisterField temperatureLowFlag;
165         private IFlagRegisterField temperatureHighFlag;
166 
167         private const sbyte defaultLowThreshold = -10;
168         private const sbyte defaultHighThreshold = 60;
169 
170         private enum Registers : byte
171         {
172             Temperature = 0x00,
173             Configuration = 0x01,
174             TemperatureLow = 0x02,
175             TemperatureHigh = 0x03,
176         }
177     }
178 }
179