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