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