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 LIS2DS12 : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor, ITemperatureSensor 23 { LIS2DS12()24 public LIS2DS12() 25 { 26 RegistersCollection = new ByteRegisterCollection(this); 27 IRQ = new GPIO(); 28 DefineRegisters(); 29 } 30 FinishTransmission()31 public void FinishTransmission(){} 32 Reset()33 public void Reset() 34 { 35 RegistersCollection.Reset(); 36 IRQ.Set(false); 37 regAddress = 0; 38 } 39 Write(byte[] data)40 public void Write(byte[] data) 41 { 42 if(data.Length == 0) 43 { 44 this.Log(LogLevel.Warning, "Unexpected write with no data"); 45 return; 46 } 47 48 this.Log(LogLevel.Noisy, "Write with {0} bytes of data: {1}", data.Length, Misc.PrettyPrintCollectionHex(data)); 49 regAddress = (Registers)data[0]; 50 51 if(data.Length > 1) 52 { 53 // skip the first byte as it contains register address 54 foreach(var b in data.Skip(1)) 55 { 56 this.Log(LogLevel.Noisy, "Writing 0x{0:X} to register {1} (0x{1:X})", b, regAddress); 57 RegistersCollection.Write((byte)regAddress, b); 58 } 59 } 60 else 61 { 62 this.Log(LogLevel.Noisy, "Preparing to read register {0} (0x{0:X})", regAddress); 63 readyPending.Value = true; 64 UpdateInterrupts(); 65 } 66 } 67 Read(int count)68 public byte[] Read(int count) 69 { 70 this.Log(LogLevel.Noisy, "Reading {0} bytes from register {1} (0x{1:X})", count, regAddress); 71 var result = new byte[count]; 72 readyPending.Value = false; 73 UpdateInterrupts(); 74 for(var i = 0; i < result.Length; i++) 75 { 76 result[i] = RegistersCollection.Read((byte)regAddress); 77 this.Log(LogLevel.Noisy, "Read value {0}", result[i]); 78 RegistersAutoIncrement(); 79 } 80 return result; 81 } 82 83 public decimal AccelerationX 84 { 85 get => accelarationX; 86 set 87 { 88 if (!IsAccelerationOutOfRange(value)) 89 { 90 accelarationX = value; 91 this.Log(LogLevel.Noisy, "AccelerationX set to {0}", accelarationX); 92 } 93 } 94 } 95 96 public decimal AccelerationY 97 { 98 get => accelarationY; 99 set 100 { 101 if (!IsAccelerationOutOfRange(value)) 102 { 103 accelarationY = value; 104 this.Log(LogLevel.Noisy, "AccelerationY set to {0}", accelarationY); 105 } 106 } 107 } 108 109 public decimal AccelerationZ 110 { 111 get => accelarationZ; 112 set 113 { 114 if (!IsAccelerationOutOfRange(value)) 115 { 116 accelarationZ = value; 117 this.Log(LogLevel.Noisy, "AccelerationZ set to {0}", accelarationZ); 118 } 119 } 120 } 121 122 public decimal Temperature { get; set; } 123 124 public GPIO IRQ { get; } 125 public ByteRegisterCollection RegistersCollection { get; } 126 DefineRegisters()127 private void DefineRegisters() 128 { 129 Registers.WhoAmI.Define(this, 0x43); 130 131 Registers.Control1.Define(this) //RW 132 .WithTaggedFlag("BLOCK_DATA_UPDATE", 0) 133 .WithFlag(1, out highFreqDataRateMode, name: "HIGH_FREQ_MODE_ENABLE") 134 .WithValueField(2, 2, out fullScale, name: "FULL_SCALE_SELECT") 135 .WithValueField(4, 4, out outDataRate, name: "OUTPUT_DATA_RATE"); 136 137 Registers.Control4.Define(this, 0x01) //RW 138 .WithFlag(0, out readyEnabled, name: "DATA_READY_IRQ1_ENABLE") 139 .WithTaggedFlag("FIFO_THRESHOLD_IRQ1_ENABLE", 1) 140 .WithTaggedFlag("6D_RECON_IRQ1_ENABLE", 2) 141 .WithTaggedFlag("DOUBLE_TAP_RECON_IRQ1_ENABLE", 3) 142 .WithTaggedFlag("FREE_FALL_RECON_IRQ1_ENABLE", 4) 143 .WithTaggedFlag("WAKEUP_RECON_IRQ1_ENABLE", 5) 144 .WithTaggedFlag("SINGLE_TAP_RECON_IRQ1_ENABLE", 6) 145 .WithTaggedFlag("MASTER_DATA_READY_IRQ1_ENABLE", 7) 146 .WithWriteCallback((_, __) => UpdateInterrupts()); 147 148 Registers.TemperatureOut.Define(this) 149 .WithValueField(0, 8, FieldMode.Read, name: "TEMPERATURE_SENSOR", valueProviderCallback: _ => TwoComplementSignConvert(Temperature)); 150 151 Registers.Status.Define(this) //RO 152 .WithFlag(0, out readyPending, FieldMode.Read, name: "XYZ_DATA_AVAILABLE") 153 .WithTaggedFlag("FREE_FALL_EVENT_DETECT", 1) 154 .WithTaggedFlag("CHANGE_IN_POSITION_DETECT", 2) 155 .WithTaggedFlag("SINGLE_TAP_EVENT_DETECT", 3) 156 .WithTaggedFlag("DOUBLE_TAP_EVENT_DETECT", 4) 157 .WithTaggedFlag("SLEEP_EVENT_DETECT", 5) 158 .WithTaggedFlag("WAKEUP_EVENT_DETECT", 6) 159 .WithTaggedFlag("FIFO_REACH_THRESHOLD", 7); 160 161 Registers.DataOutXLow.Define(this) 162 .WithValueField(0, 8, FieldMode.Read, name: "X_ACCEL_DATA[7:2]", valueProviderCallback: _ => Convert(AccelerationX, upperByte: false)); 163 164 Registers.DataOutXHigh.Define(this) 165 .WithValueField(0, 8, FieldMode.Read, name: "X_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationX, upperByte: true)); 166 167 Registers.DataOutYLow.Define(this) 168 .WithValueField(0, 8, FieldMode.Read, name: "Y_ACCEL_DATA[7:2]", valueProviderCallback: _ => Convert(AccelerationY, upperByte: false)); 169 170 Registers.DataOutYHigh.Define(this) 171 .WithValueField(0, 8, FieldMode.Read, name: "Y_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationY, upperByte: true)); 172 173 Registers.DataOutZLow.Define(this) 174 .WithValueField(0, 8, FieldMode.Read, name: "Z_ACCEL_DATA[7:2]", valueProviderCallback: _ => Convert(AccelerationZ, upperByte: false)); 175 176 Registers.DataOutZHigh.Define(this) 177 .WithValueField(0, 8, FieldMode.Read, name: "Z_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationZ, upperByte: true)); 178 179 Registers.StatusEventDetection.Define(this) //RO 180 .WithFlag(0, out readyPending, FieldMode.Read, name: "XYZ_DATA_AVAILABLE") 181 .WithTaggedFlag("FREE_FALL_EVENT_DETECT", 1) 182 .WithTaggedFlag("CHANGE_IN_POSITION_DETECT", 2) 183 .WithTaggedFlag("SINGLE_TAP_EVENT_DETECT", 3) 184 .WithTaggedFlag("DOUBLE_TAP_EVENT_DETECT", 4) 185 .WithTaggedFlag("SLEEP_EVENT_DETECT", 5) 186 .WithTaggedFlag("WAKEUP_EVENT_DETECT", 6) 187 .WithTaggedFlag("FIFO_REACH_THRESHOLD", 7); 188 } 189 RegistersAutoIncrement()190 private void RegistersAutoIncrement() 191 { 192 if(regAddress >= Registers.DataOutXLow && regAddress < Registers.DataOutZHigh) 193 { 194 regAddress = (Registers)((int)regAddress + 1); 195 this.Log(LogLevel.Noisy, "Auto-incrementing to the next register 0x{0:X} - {0}", regAddress); 196 } 197 } 198 UpdateInterrupts()199 private void UpdateInterrupts() 200 { 201 var status = readyEnabled.Value && readyPending.Value; 202 this.Log(LogLevel.Noisy, "Setting IRQ to {0}", status); 203 IRQ.Set(status); 204 } 205 GetSenesorSensitivity()206 private decimal GetSenesorSensitivity() 207 { 208 decimal gain = SensorSensitivity; 209 switch(fullScale.Value) 210 { 211 case (uint)FullScaleSelect.fullScale2g: 212 gain = SensorSensitivity; 213 break; 214 case (uint)FullScaleSelect.fullScale16g: 215 gain = 8 * SensorSensitivity; 216 break; 217 case (uint)FullScaleSelect.fullScale4g: 218 gain = 2 * SensorSensitivity; 219 break; 220 case (uint)FullScaleSelect.fullScale8g: 221 gain = 4 * SensorSensitivity; 222 break; 223 default: 224 gain = SensorSensitivity; 225 break; 226 } 227 return gain; 228 } 229 IsAccelerationOutOfRange(decimal acceleration)230 private bool IsAccelerationOutOfRange(decimal acceleration) 231 { 232 // This range protects from the overflow of the short variables in the 'Convert' function. 233 if (acceleration < MinAcceleration || acceleration > MaxAcceleration) 234 { 235 this.Log(LogLevel.Warning, "Acceleration is out of range, use value from the range <-19.5;19.5>"); 236 return true; 237 } 238 return false; 239 } 240 Convert(decimal value, bool upperByte)241 private byte Convert(decimal value, bool upperByte) 242 { 243 byte result = 0; 244 decimal gain = GetSenesorSensitivity(); 245 value = (value * 1000 / gain) / GravitationalConst; 246 var valueAsShort = (short)value; 247 248 if(upperByte) 249 { 250 result = (byte)(valueAsShort >> 8); 251 } 252 else 253 { 254 if(highFreqDataRateMode.Value && 255 outDataRate.Value >= (byte)DataRateModeStartRange.HighFreqDataRateStartRange && 256 outDataRate.Value < (byte)DataRateModeStartRange.LowPowerDataRateStartRange) 257 { 258 result = (byte)(valueAsShort & (byte)CoverBytes.HighFreqMode); 259 this.Log(LogLevel.Noisy, "High frequencies mode is selected."); 260 } 261 else if(outDataRate.Value >= (byte)DataRateModeStartRange.LowPowerDataRateStartRange) 262 { 263 result = (byte)(valueAsShort & (byte)CoverBytes.LowPowerMode); 264 this.Log(LogLevel.Noisy, "Low power mode is selected."); 265 } 266 else 267 { 268 result = (byte)(valueAsShort & (byte)CoverBytes.NoneExtraModes); 269 this.Log(LogLevel.Noisy, "High frequencies and low power modes aren't selected."); 270 } 271 } 272 return result; 273 } 274 TwoComplementSignConvert(decimal temp)275 private byte TwoComplementSignConvert(decimal temp) 276 { 277 byte tempAsByte = Decimal.ToByte(temp); 278 if(temp < 0) 279 { 280 byte twoComplementTemp = (byte)(~tempAsByte + 1); 281 return twoComplementTemp; 282 } 283 return tempAsByte; 284 } 285 286 private IFlagRegisterField readyPending; 287 private IFlagRegisterField readyEnabled; 288 private IFlagRegisterField highFreqDataRateMode; 289 private IValueRegisterField outDataRate; 290 private IValueRegisterField fullScale; 291 private Registers regAddress; 292 293 private decimal accelarationX; 294 private decimal accelarationY; 295 private decimal accelarationZ; 296 297 private const decimal MinAcceleration = -19.5m; 298 private const decimal MaxAcceleration = 19.5m; 299 private const decimal GravitationalConst = 9.806650m; // [m/s^2] 300 private const decimal SensorSensitivity = 0.061m; // [mg/digit] 301 302 private enum FullScaleSelect : byte 303 { 304 fullScale2g = 0x00, 305 fullScale16g = 0x01, 306 fullScale4g = 0x02, 307 fullScale8g = 0x03, 308 } 309 310 private enum DataRateModeStartRange : byte 311 { 312 HighFreqDataRateStartRange = 0x05, 313 LowPowerDataRateStartRange = 0x08, 314 } 315 316 private enum CoverBytes : byte 317 { 318 LowPowerMode = 0xC0, 319 HighFreqMode = 0xF0, 320 NoneExtraModes = 0xFA, 321 } 322 323 private enum Registers : byte 324 { 325 // Reserved: 0x00 - 0x05 326 // Reserved: 0x0D - 0x0E 327 WhoAmI = 0x0F, 328 // Reserved: 0x10 - 0x1F 329 Control1 = 0x20, 330 Control4 = 0x23, 331 TemperatureOut = 0x26, 332 Status = 0x27, 333 DataOutXLow = 0x28, 334 DataOutXHigh = 0x29, 335 DataOutYLow = 0x2A, 336 DataOutYHigh = 0x2B, 337 DataOutZLow = 0x2C, 338 DataOutZHigh = 0x2D, 339 StatusEventDetection = 0x36, 340 } 341 } 342 } 343