1 //
2 // Copyright (c) 2010-2024 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.Bus;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Utilities;
14 using Antmicro.Renode.Peripherals.Sensor;
15 
16 namespace Antmicro.Renode.Peripherals.I2C
17 {
18     public class SHT45 : II2CPeripheral, ITemperatureSensor
19     {
SHT45()20         public SHT45()
21         {
22             crc = new CRCEngine(0x31, 8, false, false, 0xFF);
23             Reset();
24         }
25 
Reset()26         public void Reset()
27         {
28             message = new byte[6];
29         }
30 
Write(byte[] data)31         public void Write(byte[] data)
32         {
33             this.Log(LogLevel.Noisy, "Write {0}", data.Select(x => x.ToString("X")).Aggregate((x, y) => x + " " + y));
34             if(data.Length == 0)
35             {
36                return;
37             }
38             if(data.Length > 1)
39             {
40                 this.Log(LogLevel.Warning, "Write too long ({0} bytes, expected 1)", data.Length);
41             }
42 
43             Registers register = (Registers)data[0];
44             switch(register)
45             {
46                 case Registers.MeasureHighPrecision:
47                 case Registers.MeasureMediumPrecision:
48                 case Registers.MeasureLowPrecision:
49                 case Registers.MeasureWithHeater200mw1s:
50                 case Registers.MeasureWithHeater200mw01s:
51                 case Registers.MeasureWithHeater110mw1s:
52                 case Registers.MeasureWithHeater110mw01s:
53                 case Registers.MeasureWithHeater20mw1s:
54                 case Registers.MeasureWithHeater20mw01s:
55                     EncodeMeasurementMessage();
56                     break;
57                 case Registers.ReadSerialNumber:
58                     EncodeSerialNumberMessage();
59                     break;
60                 case Registers.SoftReset:
61                     Reset();
62                     break;
63                 default:
64                     this.Log(LogLevel.Warning, "Invalid register {0}", register);
65                     break;
66             }
67         }
68 
Read(int count = 0)69         public byte[] Read(int count = 0)
70         {
71             if(count > message.Length)
72             {
73                 this.Log(LogLevel.Warning, "Trying to read too many bytes ({0} bytes, available {1})", count, message.Length);
74             }
75             byte[] buf = new byte[count];
76             Array.Copy(message, buf, count);
77             this.Log(LogLevel.Noisy, "Read {0}", buf.Select(x => x.ToString("X")).Aggregate((x, y) => x + " " + y));
78             return buf;
79         }
80 
81         //we are required to implement this method, but in case of this device there is nothing we want to do here
FinishTransmission()82         public void FinishTransmission()
83         {
84         }
85 
86         public uint SerialNumber { get; set; }
87         public decimal Temperature { get; set; }
88         public double Humidity { get; set; }
89 
EncodeSerialNumberMessage()90         private void EncodeSerialNumberMessage()
91         {
92             message[0] = (byte)((SerialNumber >> 24));
93             message[1] = (byte)((SerialNumber >> 16));
94             message[2] = (byte)crc.Calculate(new ArraySegment<byte>(message, 0, 2));
95             message[3] = (byte)((SerialNumber >> 8));
96             message[4] = (byte)(SerialNumber);
97             message[5] = (byte)crc.Calculate(new ArraySegment<byte>(message, 3, 2));
98         }
99 
EncodeMeasurementMessage()100         private void EncodeMeasurementMessage()
101         {
102             var temp = EncodeTemperature(Temperature);
103             byte tempCrc = (byte)crc.Calculate(temp);
104 
105             var rh = EncodeHumidity(Humidity);
106             byte rhCrc = (byte)crc.Calculate(rh);
107 
108             temp.CopyTo(message, 0);
109             message[2] = tempCrc;
110             rh.CopyTo(message, 3);
111             message[5] = rhCrc;
112         }
113 
EncodeTemperature(decimal temperature)114         private byte[] EncodeTemperature(decimal temperature)
115         {
116             decimal st = (temperature + 45) * 65535.0m / 175.0m;
117             ushort stU16 = (ushort)Math.Round(st);
118             byte stLo = (byte)(stU16);
119             byte stHi = (byte)((stU16 >> 8));
120 
121             return new byte[2] {stHi, stLo};
122         }
123 
EncodeHumidity(double humidity)124         private byte[] EncodeHumidity(double humidity)
125         {
126             double Srh = (humidity + 6) * 65535.0 / 125.0;
127             UInt16 SrhU16 = Convert.ToUInt16(Math.Round(Srh));
128             byte SrhLo = (byte)(SrhU16);
129             byte SrhHi = (byte)((SrhU16 >> 8));
130 
131             return new byte[2] {SrhHi, SrhLo};
132         }
133 
134         private byte[] message;
135         readonly private CRCEngine crc;
136 
137         private enum Registers
138         {
139             MeasureWithHeater20mw01s = 0x15,
140             MeasureWithHeater20mw1s = 0x1E,
141             MeasureWithHeater110mw01s = 0x24,
142             MeasureWithHeater110mw1s = 0x2F,
143             MeasureWithHeater200mw01s = 0x32,
144             MeasureWithHeater200mw1s = 0x39,
145             ReadSerialNumber = 0x89,
146             SoftReset = 0x94,
147             MeasureLowPrecision = 0xE0,
148             MeasureMediumPrecision = 0xF6,
149             MeasureHighPrecision = 0xFD
150         }
151     }
152 }
153 
154