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 Antmicro.Renode.Core.Structure.Registers; 10 using Antmicro.Renode.Exceptions; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Peripherals.I2C; 13 using Antmicro.Renode.Peripherals.Sensor; 14 using Antmicro.Renode.Utilities; 15 16 namespace Antmicro.Renode.Peripherals.Sensors 17 { 18 public class LSM9DS1_Magnetic: II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor 19 { LSM9DS1_Magnetic()20 public LSM9DS1_Magnetic() 21 { 22 fifo = new SensorSamplesFifo<Vector3DSample>(); 23 RegistersCollection = new ByteRegisterCollection(this); 24 25 DefineRegisters(); 26 } 27 Reset()28 public void Reset() 29 { 30 RegistersCollection.Reset(); 31 32 magneticSensitivity = Sensitivity.Gauss4; 33 34 address = 0; 35 addressAutoIncrement = false; 36 state = State.Idle; 37 } 38 FinishTransmission()39 public void FinishTransmission() 40 { 41 this.NoisyLog("Finishing transmission, going to the Idle state"); 42 state = State.Idle; 43 } 44 Write(byte[] data)45 public void Write(byte[] data) 46 { 47 this.Log(LogLevel.Noisy, "Written {0} bytes: {1}", data.Length, Misc.PrettyPrintCollectionHex(data)); 48 foreach(var b in data) 49 { 50 WriteByte(b); 51 } 52 } 53 WriteByte(byte b)54 public void WriteByte(byte b) 55 { 56 switch(state) 57 { 58 case State.Idle: 59 address = BitHelper.GetValue(b, offset: 0, size: 7); 60 addressAutoIncrement = BitHelper.IsBitSet(b, 7); 61 this.Log(LogLevel.Noisy, "Setting register address to {0} (0x{0:X})", (Registers)address); 62 state = State.Processing; 63 break; 64 65 case State.Processing: 66 this.Log(LogLevel.Noisy, "Writing value 0x{0:X} to register {1} (0x{1:X})", b, (Registers)address); 67 RegistersCollection.Write(address, b); 68 TryIncrementAddress(); 69 break; 70 71 default: 72 throw new ArgumentException($"Unexpected state: {state}"); 73 } 74 } 75 Read(int count = 1)76 public byte[] Read(int count = 1) 77 { 78 switch(address) 79 { 80 case (byte)Registers.OutputXLow: 81 { 82 // magnetic data is not queued 83 fifo.TryDequeueNewSample(); 84 } 85 break; 86 } 87 88 var result = RegistersCollection.Read(address); 89 this.NoisyLog("Reading register {1} (0x{1:X}) from device: 0x{0:X}", result, (Registers)address); 90 TryIncrementAddress(); 91 92 return new byte [] { result }; 93 } 94 FeedMagneticSample(decimal x, decimal y, decimal z, uint repeat = 1)95 public void FeedMagneticSample(decimal x, decimal y, decimal z, uint repeat = 1) 96 { 97 var sample = new Vector3DSample(x, y, z); 98 99 for(var i = 0; i < repeat; i++) 100 { 101 fifo.FeedSample(sample); 102 } 103 } 104 FeedMagneticSample(string path)105 public void FeedMagneticSample(string path) 106 { 107 fifo.FeedSamplesFromFile(path); 108 } 109 110 // NOTE: The meaning of this field 111 // is the default value of the channel 112 // when there are no samples in the fifo 113 public decimal MagneticX 114 { 115 get => fifo.DefaultSample.X; 116 set 117 { 118 fifo.DefaultSample.X = value; 119 } 120 } 121 122 // NOTE: The meaning of this field 123 // is the default value of the channel 124 // when there are no samples in the fifo 125 public decimal MagneticY 126 { 127 get => fifo.DefaultSample.Y; 128 set 129 { 130 fifo.DefaultSample.Y = value; 131 } 132 } 133 134 // NOTE: The meaning of this field 135 // is the default value of the channel 136 // when there are no samples in the fifo 137 public decimal MagneticZ 138 { 139 get => fifo.DefaultSample.Z; 140 set 141 { 142 fifo.DefaultSample.Z = value; 143 } 144 } 145 146 public ByteRegisterCollection RegistersCollection { get; } 147 TryIncrementAddress()148 private void TryIncrementAddress() 149 { 150 if(!addressAutoIncrement) 151 { 152 return; 153 } 154 address = (byte)((address + 1) % 0x80); 155 } 156 DefineRegisters()157 private void DefineRegisters() 158 { 159 Registers.OutputXLow.Define(this) 160 .WithValueField(0, 8, FieldMode.Read, name: "OUT_X_L_M", valueProviderCallback: _ => GetScaledValue(MagneticX, (short)magneticSensitivity, upperByte: false)) 161 ; 162 163 Registers.OutputXHigh.Define(this) 164 .WithValueField(0, 8, FieldMode.Read, name: "OUT_X_H_M", valueProviderCallback: _ => GetScaledValue(MagneticX, (short)magneticSensitivity, upperByte: true)) 165 ; 166 167 Registers.OutputYLow.Define(this) 168 .WithValueField(0, 8, FieldMode.Read, name: "OUT_Y_L_M", valueProviderCallback: _ => GetScaledValue(MagneticY, (short)magneticSensitivity, upperByte: false)) 169 ; 170 171 Registers.OutputYHigh.Define(this) 172 .WithValueField(0, 8, FieldMode.Read, name: "OUT_Y_H_M", valueProviderCallback: _ => GetScaledValue(MagneticY, (short)magneticSensitivity, upperByte: true)) 173 ; 174 175 Registers.OutputZLow.Define(this) 176 .WithValueField(0, 8, FieldMode.Read, name: "OUT_Z_L_M", valueProviderCallback: _ => GetScaledValue(MagneticZ, (short)magneticSensitivity, upperByte: false)) 177 ; 178 179 Registers.OutputZHigh.Define(this) 180 .WithValueField(0, 8, FieldMode.Read, name: "OUT_Z_H_M", valueProviderCallback: _ => GetScaledValue(MagneticZ, (short)magneticSensitivity, upperByte: true)) 181 ; 182 183 Registers.WhoAmI.Define(this, 0x3d); 184 185 Registers.Control2.Define(this) 186 .WithReservedBits(0, 2) 187 .WithTag("SOFT_RST", 2, 1) 188 .WithTag("REBOOT", 3, 1) 189 .WithReservedBits(4, 1) 190 .WithValueField(5, 2, name: "FS: Magnetometer full-scale selection", 191 valueProviderCallback: _ => 192 { 193 switch(magneticSensitivity) 194 { 195 case Sensitivity.Gauss4: 196 return 0; 197 case Sensitivity.Gauss8: 198 return 1; 199 case Sensitivity.Gauss12: 200 return 2; 201 case Sensitivity.Gauss16: 202 return 3; 203 default: 204 throw new ArgumentException("This should never happen"); 205 } 206 }, 207 writeCallback: (_, val) => 208 { 209 switch(val) 210 { 211 case 0: 212 magneticSensitivity = Sensitivity.Gauss4; 213 break; 214 case 1: 215 magneticSensitivity = Sensitivity.Gauss8; 216 break; 217 case 2: 218 magneticSensitivity = Sensitivity.Gauss12; 219 break; 220 case 3: 221 magneticSensitivity = Sensitivity.Gauss16; 222 break; 223 } 224 }) 225 .WithReservedBits(7, 1) 226 ; 227 } 228 GetScaledValue(decimal value, short sensitivity, bool upperByte)229 private byte GetScaledValue(decimal value, short sensitivity, bool upperByte) 230 { 231 var scaled = (short)(value * sensitivity); 232 return upperByte 233 ? (byte)(scaled >> 8) 234 : (byte)scaled; 235 } 236 237 private byte address; 238 private bool addressAutoIncrement; 239 private State state; 240 private Sensitivity magneticSensitivity = Sensitivity.Gauss4; 241 242 private readonly SensorSamplesFifo<Vector3DSample> fifo; 243 244 private enum Sensitivity : ushort 245 { 246 Gauss4 = 8192, 247 Gauss8 = 4096, 248 Gauss12 = 3072, 249 Gauss16 = 2048 250 } 251 252 private enum State 253 { 254 Idle, 255 Processing 256 } 257 258 private enum Registers 259 { 260 // 0x0 - 0x04 are reserved 261 OffsetXLow = 0x05, 262 OffsetXHigh = 0x06, 263 OffsetYLow = 0x07, 264 OffsetYHigh = 0x08, 265 OffsetZLow = 0x09, 266 OffsetZHigh = 0x0A, 267 268 // 0x0B - 0x0E are reserved 269 WhoAmI = 0x0F, 270 271 // 0x10 - 0x1F are reserved 272 Control1 = 0x20, 273 Control2 = 0x21, 274 Control3 = 0x22, 275 Control4 = 0x23, 276 Control5 = 0x24, 277 278 // 0x25 - 0x26 are reserved 279 Status = 0x27, 280 OutputXLow = 0x28, 281 OutputXHigh = 0x29, 282 OutputYLow = 0x2A, 283 OutputYHigh = 0x2B, 284 OutputZLow = 0x2C, 285 OutputZHigh = 0x2D, 286 287 // 0x2E - 0x2F are reserved 288 InterruptConfig = 0x30, 289 InterruptSource = 0x31, 290 InterruptThresholdLow = 0x32, 291 InterruptThresholdHigh = 0x33 292 } 293 } 294 } 295