1 // 2 // Copyright (c) 2010-2020 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 8 using System; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Linq; 12 using Antmicro.Renode.Core; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals.I2C; 16 using Antmicro.Renode.Peripherals.Sensor; 17 using Antmicro.Renode.Utilities; 18 using Antmicro.Renode.Exceptions; 19 20 namespace Antmicro.Renode.Peripherals.Sensors 21 { 22 public class LSM303DLHC_Gyroscope : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor, ITemperatureSensor 23 { LSM303DLHC_Gyroscope()24 public LSM303DLHC_Gyroscope() 25 { 26 RegistersCollection = new ByteRegisterCollection(this); 27 DefineRegisters(); 28 } 29 FinishTransmission()30 public void FinishTransmission() 31 { 32 regAddress = 0; 33 } 34 Reset()35 public void Reset() 36 { 37 RegistersCollection.Reset(); 38 regAddress = 0; 39 } 40 Write(byte[] data)41 public void Write(byte[] data) 42 { 43 if(data.Length == 0) 44 { 45 this.Log(LogLevel.Warning, "Unexpected write with no data"); 46 return; 47 } 48 49 this.Log(LogLevel.Noisy, "Write with {0} bytes of data", data.Length); 50 regAddress = (Registers)data[0]; 51 52 if(data.Length > 1) 53 { 54 // skip the first byte as it contains register address 55 foreach(var b in data.Skip(1)) 56 { 57 this.Log(LogLevel.Debug,"Writing 0x{0:X} to register {1} (0x{1:X})", b, regAddress); 58 RegistersCollection.Write((byte)regAddress, b); 59 regAddress = (Registers)((int)regAddress + 1); 60 } 61 } 62 else 63 { 64 this.Log(LogLevel.Noisy, "Preparing to read register {0} (0x{0:X})", regAddress); 65 dataReady.Value = IsInSleepMode(); 66 dataLock.Value = false; 67 } 68 } 69 Read(int count)70 public byte[] Read(int count) 71 { 72 this.Log(LogLevel.Debug, "Reading {0} bytes from register {1} (0x{1:X})", count, regAddress); 73 var result = new byte[count]; 74 for(var i = 0; i < result.Length; i++) 75 { 76 if(regAddress == Registers.DataOutXH) 77 { 78 dataLock.Value = true; 79 } 80 result[i] = RegistersCollection.Read((byte)regAddress); 81 this.Log(LogLevel.Noisy, "Read value {0}", result[i]); 82 regAddress = (Registers)((int)regAddress + 1); 83 } 84 return result; 85 } 86 87 public decimal Temperature 88 { 89 get => temperature; 90 set 91 { 92 if(value < MinTemperature || value > MaxTemperature) 93 { 94 this.Log(LogLevel.Warning, "Temperature is out of range"); 95 } 96 else 97 { 98 temperature = value; 99 this.Log(LogLevel.Noisy, "Sensor temperature set to {0}", temperature); 100 } 101 } 102 } 103 104 public decimal MagneticFieldX 105 { 106 get => magneticFieldX; 107 set 108 { 109 if(!IsMagneticFieldOutOfRange(value)) 110 { 111 magneticFieldX = value; 112 this.Log(LogLevel.Noisy, "MagneticFieldX set to {0}", magneticFieldX); 113 } 114 } 115 } 116 117 public decimal MagneticFieldY 118 { 119 get => magneticFieldY; 120 set 121 { 122 if(!IsMagneticFieldOutOfRange(value)) 123 { 124 magneticFieldY = value; 125 this.Log(LogLevel.Noisy, "MagneticFieldY set to {0}", magneticFieldY); 126 } 127 } 128 } 129 130 public decimal MagneticFieldZ 131 { 132 get => magneticFieldZ; 133 set 134 { 135 if(!IsMagneticFieldOutOfRange(value)) 136 { 137 magneticFieldZ = value; 138 this.Log(LogLevel.Noisy, "MagneticFieldZ set to {0}", magneticFieldZ); 139 } 140 } 141 } 142 143 public ByteRegisterCollection RegistersCollection { get; } 144 DefineRegisters()145 private void DefineRegisters() 146 { 147 Registers.ConfigA.Define(this) //RW 148 .WithTag("PREREQ1", 0, 2) 149 .WithTag("DATA_RATE_BITS", 2, 3) 150 .WithTag("PREREQ2", 5, 2) 151 .WithFlag(7, out temperatureSensorEnable, name: "TEMP_SENSOR_ENABLE"); 152 153 Registers.ConfigB.Define(this) //RW 154 .WithTag("PREREQ", 0, 5) 155 .WithValueField(5, 3, out gainConfiguration, name: "GAIN"); 156 157 Registers.Mode.Define(this) //RW 158 .WithValueField(0, 2, out sensorOperatingMode, name: "MODE_SELECT") 159 .WithTag("PREREQ", 2, 6); 160 161 Registers.DataOutXH.Define(this) 162 .WithValueField(0, 8, FieldMode.Read, name: "X_MAGNETIC_FIELD_DATA[15:8]", valueProviderCallback: _ => Convert(MagneticFieldX, upperByte: true)); 163 164 Registers.DataOutXL.Define(this) 165 .WithValueField(0, 8, FieldMode.Read, name: "X_MAGNETIC_FIELD_DATA[7:0]", valueProviderCallback: _ => Convert(MagneticFieldX, upperByte: false)); 166 167 Registers.DataOutZH.Define(this) 168 .WithValueField(0, 8, FieldMode.Read, name: "Z_MAGNETIC_FIELD_DATA[15:8]", valueProviderCallback: _ => Convert(MagneticFieldZ, upperByte: true)); 169 170 Registers.DataOutZL.Define(this) 171 .WithValueField(0, 8, FieldMode.Read, name: "Z_MAGNETIC_FIELD_DATA[7:0]", valueProviderCallback: _ => Convert(MagneticFieldZ, upperByte: false)); 172 173 Registers.DataOutYH.Define(this) 174 .WithValueField(0, 8, FieldMode.Read, name: "Y_MAGNETIC_FIELD_DATA[15:8]", valueProviderCallback: _ => Convert(MagneticFieldY, upperByte: true)); 175 176 Registers.DataOutYL.Define(this) 177 .WithValueField(0, 8, FieldMode.Read, name: "Y_MAGNETIC_FIELD_DATA[7:0]", valueProviderCallback: _ => Convert(MagneticFieldY, upperByte: false)); 178 179 Registers.DataReady.Define(this, 0x01) //RO 180 .WithFlag(0, out dataReady, FieldMode.Read, name: "DATA_READY") 181 .WithFlag(1, out dataLock, FieldMode.Read, name: "DATA_LOCK") 182 .WithReservedBits(2, 6); 183 184 Registers.TemperatureDataOutH.Define(this) 185 .WithValueField(0, 8, FieldMode.Read, name: "TEMP_DATA[11:4]", valueProviderCallback: _ => ConvertTemperature(temperature, upperByte: true)); 186 187 Registers.TemperatureDataOutL.Define(this) 188 .WithReservedBits(0, 4) 189 .WithValueField(4, 4, FieldMode.Read, name: "TEMP_DATA[3:0]", valueProviderCallback: _ => ConvertTemperature(temperature, upperByte: false)); 190 } 191 IsInSleepMode()192 private bool IsInSleepMode() 193 { 194 if(sensorOperatingMode.Value == (uint)OperatingModes.SleepMode0 || 195 sensorOperatingMode.Value == (uint)OperatingModes.SleepMode1) 196 { 197 this.Log(LogLevel.Debug, "Sensor is placed in sleep mode"); 198 return false; 199 } 200 else 201 { 202 return true; 203 } 204 } 205 GetGain()206 private ushort GetGain() 207 { 208 ushort gain = 0; // [LSB/Gauss] 209 switch(gainConfiguration.Value) 210 { 211 case 0: 212 gain = 980; 213 break; 214 case 1: 215 gain = 1100; 216 break; 217 case 2: 218 gain = 760; 219 break; 220 case 3: 221 gain = 600; 222 break; 223 case 4: 224 gain = 400; 225 break; 226 case 5: 227 gain = 355; 228 break; 229 case 6: 230 gain = 295; 231 break; 232 case 7: 233 gain = 205; 234 break; 235 default: 236 this.Log(LogLevel.Warning, "Unsupported value of sensor gain."); 237 break; 238 } 239 return gain; 240 } 241 IsMagneticFieldOutOfRange(decimal magneticField)242 private bool IsMagneticFieldOutOfRange(decimal magneticField) 243 { 244 // This range protects from the overflow of the short variables in the 'Convert' function. 245 if(magneticField < MinMagneticField || magneticField > MaxMagneticField) 246 { 247 this.Log(LogLevel.Warning, "MagneticField is out of range, use value from the range <{0};{1}>", 248 MinMagneticField, MaxMagneticField); 249 return true; 250 } 251 return false; 252 } 253 Convert(decimal value, bool upperByte)254 private byte Convert(decimal value, bool upperByte) 255 { 256 decimal convertedValue = (decimal)(value * GetGain()); 257 short convertedValueAsShort = (short)convertedValue; 258 return upperByte ? (byte)(convertedValueAsShort >> 8) : (byte)convertedValueAsShort; 259 } 260 ConvertTemperature(decimal value, bool upperByte)261 private byte ConvertTemperature(decimal value, bool upperByte) 262 { 263 if(!temperatureSensorEnable.Value) 264 { 265 this.Log(LogLevel.Warning, "Temperature sensor disable"); 266 return 0x00; 267 } 268 return upperByte ? (byte)((short)value >> 8) : (byte)((short)value >> 4) ; 269 } 270 271 private decimal temperature; 272 private Registers regAddress; 273 private IFlagRegisterField temperatureSensorEnable; 274 private IFlagRegisterField dataReady; 275 private IFlagRegisterField dataLock; 276 private IValueRegisterField sensorOperatingMode; 277 private IValueRegisterField gainConfiguration; 278 private decimal magneticFieldX; 279 private decimal magneticFieldY; 280 private decimal magneticFieldZ; 281 282 private const decimal MinMagneticField = -29.5m; 283 private const decimal MaxMagneticField = 29.5m; 284 private const decimal MinTemperature = -40; 285 private const decimal MaxTemperature = 85; 286 287 private enum OperatingModes : byte 288 { 289 ContiniousConversionMode = 0x0, 290 SingleConversionMode = 0x1, 291 SleepMode0 = 0x2, 292 SleepMode1 = 0x3, 293 } 294 295 private enum Registers 296 { 297 // Magnetic field sensing registers: 298 ConfigA = 0x00, 299 ConfigB = 0x01, 300 Mode = 0x02, 301 DataOutXH = 0x03, 302 DataOutXL = 0x04, 303 DataOutZH = 0x05, 304 DataOutZL = 0x06, 305 DataOutYH = 0x07, 306 DataOutYL = 0x08, 307 DataReady = 0x09, 308 InterruptA = 0x0A, 309 InterruptB = 0x0B, 310 InterruptC = 0x0C, 311 // Reserved: 0x0D - 0x30 312 TemperatureDataOutH = 0x31, 313 TemperatureDataOutL = 0x32, 314 // Reserved: 0x33 - 0x3A 315 } 316 } 317 } 318