1 //
2 // Copyright (c) 2010-2020 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using System.Linq;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Core;
14 
15 namespace Antmicro.Renode.Peripherals.I2C
16 {
17     public class SHT21 : II2CPeripheral
18     {
19         public double MeanHumidity { get; set; }
SHT21()20         public SHT21()
21         {
22             MeanHumidity = 75.0;
23             Reset();
24         }
25 
Reset()26         public void Reset()
27         {
28             this.Log(LogLevel.Noisy, "Reset registers");
29             user = 0x3A;
30             state = (uint)States.Idle;
31             registerAddress = 0;
32             registerData = 0;
33             resultArray[0] = 0;
34             resultArray[1] = 0;
35         }
36 
Write(byte[] data)37         public void Write(byte[] data)
38         {
39             // Parse the list bytes
40             if(data.Length < 2)
41             {
42                 // Must always have mode and register address in list
43                 this.Log(LogLevel.Noisy, "Write - too few elements in list ({0}) - must be at least two", data.Length);
44                 return;
45             }
46             this.NoisyLog("Write {0}", data.Select(x => x.ToString("X")).Aggregate((x, y) => x + " " + y));
47             // First byte sets the device state
48             state = data[0];
49             this.Log(LogLevel.Noisy, "State changed to {0}", (States)state);
50             // Second byte is always register address
51             registerAddress = data[1];
52             if(data.Length == 3)
53             {
54                 // Got a value to write to register
55                 registerData = data[2];
56             }
57             var result = new byte[3] { 0, 0, 0 };
58             switch((States)state)
59             {
60             case States.ReceivingData:
61                 switch((Registers)registerAddress)
62                 {
63                 case Registers.SoftReset:
64                     Reset();
65                     break;
66                 case Registers.UserWrite:
67                     user = registerData;
68                     break;
69                 case Registers.UserRead:
70                     break;
71                 case Registers.TemperatureHM:
72                     GetTemperature();
73                     break;
74                 case Registers.TemperaturePoll:
75                     GetTemperature();
76                     // Polling issues Write and then reads directly without writing read command
77                     // so it is necessary to prepare send data here
78                     result = new byte[3] { 0, 0, 0 };
79                     result[0] = resultArray[0];
80                     result[1] = resultArray[1];
81                     result[2] = GetSTH21CRC(resultArray, 2);
82                     sendData = new byte[result.Length + 1];
83                     result.CopyTo(sendData, 0);
84                     sendData[result.Length] = GetCRC(data, result);
85                     break;
86                 case Registers.HumidityHM:
87                     GetHumidity();
88                     break;
89                 case Registers.HumidityPoll:
90                     GetHumidity();
91                     // Polling issues Write and then reads directly without writing read command
92                     // so it is necessary to prepare send data here
93                     result = new byte[3] { 0, 0, 0 };
94                     result[0] = resultArray[0];
95                     result[1] = resultArray[1];
96                     result[2] = GetSTH21CRC(resultArray, 2);
97                     sendData = new byte[result.Length + 1];
98                     result.CopyTo(sendData, 0);
99                     sendData[result.Length] = GetCRC(data, result);
100                     break;
101                 case Registers.OnChipMemory1:
102                     // registerData = 0x0F (on-chip memory address)
103                     // Prepare serial number for read
104                     // SNB_3, CRC SNB_3, SNB_2, CRC SNB_2, SNB_1, CRC SNB_1, SNB_0, CRC SNB_0
105                     break;
106                 case Registers.OnChipMemory2:
107                     // registerData = 0xC9 (on-chip memory address)
108                     // Prepare serial number for read
109                     // SNC_1, SNC_0, CRC SNC0/1, SNA_1, SNA_0, CRC SNA_0/1
110                     break;
111                 default:
112                     this.Log(LogLevel.Noisy, "Register address invalid - no action");
113                     break;
114                 }
115                 state = (uint)States.Idle;
116                 this.Log(LogLevel.Noisy, "State changed to Idle");
117                 break;
118             case States.SendingData:
119                 switch((Registers)registerAddress)
120                 {
121                 case Registers.TemperaturePoll:
122                 // Should not happen - fall through just in case
123                 case Registers.TemperatureHM:
124                     result = new byte[3] { 0, 0, 0 };
125                     result[0] = resultArray[0];
126                     result[1] = resultArray[1];
127                     result[2] = GetSTH21CRC(resultArray, 2);
128                     break;
129                 case Registers.HumidityPoll:
130                 // Should not happen - fall through just in case
131                 case Registers.HumidityHM:
132                     result = new byte[3] { 0, 0, 0 };
133                     result[0] = resultArray[0];
134                     result[1] = resultArray[1];
135                     result[2] = GetSTH21CRC(resultArray, 2);
136                     break;
137                 case Registers.UserRead:
138                     result = new byte[1] { 0 };
139                     result[0] = user;
140                     break;
141                 case Registers.OnChipMemory1:
142                     // Add serial number for read
143                     // SNB_3, CRC SNB_3, SNB_2, CRC SNB_2, SNB_1, CRC SNB_1, SNB_0, CRC SNB_0
144                     result = new byte[9] { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
145                     break;
146                 case Registers.OnChipMemory2:
147                     // Add serial number for read
148                     // SNC_1, SNC_0, CRC SNC0/1, SNA_1, SNA_0, CRC SNA_0/1
149                     result = new byte[7] { 0, 0, 0, 0, 0, 0, 0 };
150                     break;
151                 default:
152                     break;
153                 }
154                 sendData = new byte[result.Length + 1];
155                 result.CopyTo(sendData, 0);
156                 sendData[result.Length] = GetCRC(data, result);
157                 break;
158             default:
159                 break;
160             }
161         }
162 
Read(int count = 0)163         public byte[] Read(int count = 0)
164         {
165             this.NoisyLog("Read {0}", sendData.Select(x => x.ToString("X")).Aggregate((x, y) => x + " " + y));
166             return sendData;
167         }
168 
FinishTransmission()169         public void FinishTransmission()
170         {
171         }
172 
SensorData(double mean, double sigma)173         private double SensorData(double mean, double sigma)
174         {
175             // mean = mean value of Gaussian (Normal) distribution and sigma = standard deviation
176             int sign = random.Next(10);
177             double x;
178             if(sign > 5)
179             {
180                 x = mean * (1.0 + random.NextDouble() / (2 * sigma));
181             }
182             else
183             {
184                 x = mean * (1.0 - random.NextDouble() / (2 * sigma));
185             }
186             return x;
187         }
188 
GetTemperature()189         private void GetTemperature()
190         {
191             // Temperature in degrees Centigrade are calculated as:
192             // T = -46.85 + 175.72 * ST/2^16
193             // Return ST in two bytes, 14, 12, 13 or 11 bits precision
194             // bits 0 and 1 in LSB are status bits
195             // bit 1 indicates measurement type, 0 for Temperature, 1 for Humidity
196             // bit 0 is currently not assigned - shall be zero
197             // bits 2 and 3 unused - shall be zero
198             // TODO: T is scaled to avoid truncating since precision < 16 bits
199             // Generated sensor data needs to be verified against real hardware
200             double temperature = SensorData(25.0, 1.0);
201             double result = (temperature + 46.85) * 65536.0 / 17572.0;
202             UInt16 resultInt = Convert.ToUInt16(Math.Round(result));
203             // Handle different precision as specified in user register bit7,0
204             switch((UserControls)(user & (int)UserControls.ResolutionMask))
205             {
206             case UserControls.Resolution_12_14BIT:
207                 resultArray[0] = (byte)((resultInt >> 6) & 0xFF);
208                 resultArray[1] = (byte)((resultInt & 0x3F) << 2);
209                 break;
210             case UserControls.Resolution_8_12BIT:
211                 resultArray[0] = (byte)((resultInt >> 4) & 0xFF);
212                 resultArray[1] = (byte)((resultInt & 0xF) << 4);
213                 break;
214             case UserControls.Resolution_10_13BIT:
215                 resultArray[0] = (byte)((resultInt >> 5) & 0xFF);
216                 resultArray[1] = (byte)((resultInt & 0x1F) << 3);
217                 break;
218             case UserControls.Resolution_11_11BIT:
219                 resultArray[0] = (byte)((resultInt >> 3) & 0xFF);
220                 resultArray[1] = (byte)((resultInt & 0x7) << 5);
221                 break;
222             default:
223                 break;
224             }
225         }
226 
GetHumidity()227         private void GetHumidity()
228         {
229             // Relative humidity in percent
230             // RH = -6 + 125 * SRH/2^16
231             // Return SRH in two bytes, 12, 8, 10 or 11 bits precision
232             // bits 0 and 1 in LSB are status bits
233             // bit 1 indicates measurement type, 0 for Temperature, 1 for Humidity
234             // bit 0 is currently not assigned - shall be zero
235             // bits 2 and 3 unused - shall be zero
236             double humidity = SensorData(MeanHumidity, 5.0);
237             if(humidity > 99.0)
238             {
239                 humidity = 99.0;
240             }
241             if(humidity < 1.0)
242             {
243                 humidity = 1.0;
244             }
245             double result = (humidity + 6.0) * 65536.0 / 125;
246             UInt16 resultInt = Convert.ToUInt16(Math.Round(result));
247             // Handle different precision as specified in user register bit7,0
248             switch((UserControls)(user & (int)UserControls.ResolutionMask))
249             {
250             case UserControls.Resolution_12_14BIT:
251                 resultArray[0] = (byte)((resultInt >> 4) & 0xFF);
252                 resultArray[1] = (byte)(((resultInt & 0xF) << 4) + 0x2);
253                 break;
254             case UserControls.Resolution_8_12BIT:
255                 resultArray[0] = (byte)((resultInt) & 0xFF);
256                 resultArray[1] = 0x2;
257                 break;
258             case UserControls.Resolution_10_13BIT:
259                 resultArray[0] = (byte)((resultInt >> 2) & 0xFF);
260                 resultArray[1] = (byte)(((resultInt & 0x3) << 6) + 0x2);
261                 break;
262             case UserControls.Resolution_11_11BIT:
263                 resultArray[0] = (byte)(((resultInt) >> 3) & 0xFF);
264                 resultArray[1] = (byte)(((resultInt & 0x7) << 5) + 0x2);
265                 break;
266             default:
267                 break;
268             }
269         }
270 
GetSTH21CRC(byte[] array, int nrOfBytes)271         private byte GetSTH21CRC(byte[] array, int nrOfBytes)
272         {
273             const uint POLYNOMIAL = 0x131;  // P(x)=x^8+x^5+x^4+1 = 100110001
274             byte crc = 0;
275             for(byte i = 0; i < nrOfBytes; ++i)
276             {
277                 crc ^= (array[i]);
278                 for(byte bit = 8; bit > 0; --bit)
279                 {
280                     if((crc & 0x80) == 0x80)
281                     {
282                         crc = (byte)(((uint)crc << 1) ^ POLYNOMIAL);
283                     }
284                     else
285                     {
286                         crc = (byte)(crc << 1);
287                     }
288                 }
289             }
290             return crc;
291         }
292 
GetCRC(byte[] input, byte[] output)293         private byte GetCRC(byte[] input, byte[] output)
294         {
295             var crc = input[0];
296             for(int i = 1; i < input.Length; i++)
297             {
298                 crc ^= input[i];
299             }
300             for(int j = 0; j < output.Length; j++)
301             {
302                 crc ^= output[j];
303             }
304             return crc;
305         }
306 
307         private byte[] resultArray = new byte[2] { 0, 0 };
308         private byte user;
309 
310         private uint state;
311         private byte registerAddress;
312         private byte registerData;
313         private byte[] sendData;
314 
315         private static PseudorandomNumberGenerator random = EmulationManager.Instance.CurrentEmulation.RandomGenerator;
316 
317         private enum UserControls
318         {
319             Resolution_12_14BIT = 0x00, // RH=12bit, T=14bit
320             Resolution_8_12BIT = 0x01, // RH= 8bit, T=12bit
321             Resolution_10_13BIT = 0x80, // RH=10bit, T=13bit
322             Resolution_11_11BIT = 0x81, // RH=11bit, T=11bit
323             ResolutionMask = 0x81  // Mask for bits 7,0 in user register
324         }
325 
326         private enum States
327         {
328             Idle = 0x0,
329             ReceivingData = 0xFD,
330             SendingData = 0xFC
331         }
332 
333         private enum Registers
334         {
335             TemperatureHM = 0xE3, // Read-Write
336             HumidityHM = 0xE5, // Read-Write
337             UserWrite = 0xE6, // Write-Only
338             UserRead = 0xE7, // Read-Only
339             TemperaturePoll = 0xF3, // Read-Write
340             HumidityPoll = 0xF5, // Read-Write
341             OnChipMemory1 = 0xFA, // Read-Only
342             OnChipMemory2 = 0xFC, // Read-Only
343             SoftReset = 0xFE  // Write-Only
344         }
345     }
346 }
347 
348