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