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 using System;
8 using System.Collections.Generic;
9 using System.IO;
10 using System.Linq;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Peripherals.I2C;
15 using Antmicro.Renode.Peripherals.Sensor;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Exceptions;
18 
19 namespace Antmicro.Renode.Peripherals.Sensors
20 {
21     public class LSM303DLHC_Accelerometer : II2CPeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, ISensor
22     {
LSM303DLHC_Accelerometer()23         public LSM303DLHC_Accelerometer()
24         {
25             RegistersCollection = new ByteRegisterCollection(this);
26             IRQ0 = new GPIO();
27             IRQ1 = new GPIO();
28             irqs[0] = IRQ0;
29             irqs[1] = IRQ1;
30             DefineRegisters();
31         }
32 
FinishTransmission()33         public void FinishTransmission()
34         {
35             registerAddress = 0;
36             multipleOperation = false;
37         }
38 
Reset()39         public void Reset()
40         {
41             RegistersCollection.Reset();
42             multipleOperation = false;
43             registerAddress = 0;
44             IRQ0.Unset();
45             IRQ1.Unset();
46         }
47 
Write(byte[] data)48         public void Write(byte[] data)
49         {
50             if(data.Length == 0)
51             {
52                 this.Log(LogLevel.Warning, "Unexpected write with no data");
53                 return;
54             }
55 
56             this.Log(LogLevel.Noisy, "Write with {0} bytes of data", data.Length);
57             // Bits 6-0 represent the first register address
58             registerAddress = (Registers)(data[0] & 0x7F);
59             // The most significant bit means multiple read/write operation
60             multipleOperation = data[0] > 0x80;
61 
62             if(data.Length > 1)
63             {
64                 // skip the first byte as it contains register address
65                 foreach(var b in data.Skip(1))
66                 {
67                     this.Log(LogLevel.Noisy, "Writing 0x{0:X} to register {1} (0x{1:X})", b, registerAddress);
68                     RegistersCollection.Write((byte)registerAddress, b);
69                     RegistersAutoIncrement();
70                 }
71             }
72             else
73             {
74                 this.Log(LogLevel.Noisy, "Preparing to read register {0} (0x{0:X})", registerAddress);
75                 if(dataRate.Value == 0)
76                 {
77                     this.Log(LogLevel.Debug, "Power-down mode is set");
78                     xyzDataAvailable.Value = false;
79                     xDataAvailable.Value = false;
80                     yDataAvailable.Value = false;
81                     zDataAvailable.Value = false;
82                 }
83                 else
84                 {
85                     if(xAxisEnable.Value && yAxisEnable.Value && zAxisEnable.Value)
86                     {
87                         xyzDataAvailable.Value = true;
88                         xDataAvailable.Value = false;
89                         yDataAvailable.Value = false;
90                         zDataAvailable.Value = false;
91                     }
92                     else
93                     {
94                         xyzDataAvailable.Value = false;
95                         xDataAvailable.Value = xAxisEnable.Value;
96                         yDataAvailable.Value = yAxisEnable.Value;
97                         zDataAvailable.Value = zAxisEnable.Value;
98                     }
99                 }
100                 UpdateInterrupts();
101             }
102         }
103 
Read(int count)104         public byte[] Read(int count)
105         {
106             this.Log(LogLevel.Debug, "Reading {0} bytes from register {1} (0x{1:X})", count, registerAddress);
107             var result = new byte[count];
108             for(var i = 0; i < result.Length; i++)
109             {
110                 result[i] = RegistersCollection.Read((byte)registerAddress);
111                 this.Log(LogLevel.Noisy, "Read value: {0}", result[i]);
112                 RegistersAutoIncrement();
113             }
114             return result;
115         }
116 
117         public decimal AccelerationX
118         {
119             get => accelarationX;
120             set
121             {
122                 if(!IsAccelerationOutOfRange(value))
123                 {
124                     accelarationX = value;
125                     this.Log(LogLevel.Noisy, "AccelerationX set to {0}", accelarationX);
126                 }
127             }
128         }
129 
130         public decimal AccelerationY
131         {
132             get => accelarationY;
133             set
134             {
135                 if(!IsAccelerationOutOfRange(value))
136                 {
137                     accelarationY = value;
138                     this.Log(LogLevel.Noisy, "AccelerationY set to {0}", accelarationY);
139                 }
140             }
141         }
142 
143         public decimal AccelerationZ
144         {
145             get => accelarationZ;
146             set
147             {
148                 if(!IsAccelerationOutOfRange(value))
149                 {
150                     accelarationZ = value;
151                     this.Log(LogLevel.Noisy, "AccelerationZ set to {0}", accelarationZ);
152                 }
153             }
154         }
155 
156         public ByteRegisterCollection RegistersCollection { get; }
157         public GPIO IRQ0 { get; }
158         public GPIO IRQ1 { get; }
159 
DefineRegisters()160         private void DefineRegisters()
161         {
162             Registers.ChipID.Define(this, 0x33);
163 
164             Registers.Control1.Define(this, 0x43) //RW
165                 .WithFlag(0, out xAxisEnable, name: "X_AXIS_ENABLE")
166                 .WithFlag(1, out yAxisEnable, name: "Y_AXIS_ENABLE")
167                 .WithFlag(2, out zAxisEnable, name: "Z_AXIS_ENABLE")
168                 .WithTaggedFlag("LOW_POWER_ENABLE", 3)
169                 .WithValueField(4, 4, out dataRate, name: "DATA_RATE");
170 
171             Registers.Control4.Define(this) //RW
172                 .WithTaggedFlag("SIM", 0)
173                 .WithTag("PREREQ", 1, 2)
174                 .WithTaggedFlag("HR", 3)
175                 .WithValueField(4, 2, out fullScale, name: "FS")
176                 .WithTaggedFlag("BLE", 6)
177                 .WithTaggedFlag("BDU", 7);
178 
179             Registers.StatusReg.Define(this, 0x08) //RO
180                 .WithFlag(0, out xDataAvailable, FieldMode.Read, name: "X_DATA_AVAILABLE")
181                 .WithFlag(1, out yDataAvailable, FieldMode.Read, name: "Y_DATA_AVAILABLE")
182                 .WithFlag(2, out zDataAvailable, FieldMode.Read, name: "Z_DATA_AVAILABLE")
183                 .WithFlag(3, out xyzDataAvailable, FieldMode.Read, name: "XYZ_DATA_AVAILABLE")
184                 .WithTaggedFlag("X_DATA_OVERRUN", 4)
185                 .WithTaggedFlag("Y_DATA_OVERRUN", 5)
186                 .WithTaggedFlag("Z_DATA_OVERRUN", 6)
187                 .WithTaggedFlag("XYZ_DATA_OVERRUN", 7);
188 
189             Registers.DataOutXL.Define(this)
190                 .WithValueField(0, 8, FieldMode.Read, name: "X_ACCEL_DATA[7:0]", valueProviderCallback: _ => Convert(AccelerationX, upperByte: false));
191 
192             Registers.DataOutXH.Define(this)
193                 .WithValueField(0, 8, FieldMode.Read, name: "X_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationX, upperByte: true));
194 
195             Registers.DataOutYL.Define(this)
196                 .WithValueField(0, 8, FieldMode.Read, name: "Y_ACCEL_DATA[7:0]", valueProviderCallback: _ => Convert(AccelerationY, upperByte: false));
197 
198             Registers.DataOutYH.Define(this)
199                 .WithValueField(0, 8, FieldMode.Read, name: "Y_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationY, upperByte: true));
200 
201             Registers.DataOutZL.Define(this)
202                 .WithValueField(0, 8, FieldMode.Read, name: "Z_ACCEL_DATA[7:0]", valueProviderCallback: _ => Convert(AccelerationZ, upperByte: false));
203 
204             Registers.DataOutZH.Define(this)
205                 .WithValueField(0, 8, FieldMode.Read, name: "Z_ACCEL_DATA[15:8]", valueProviderCallback: _ => Convert(AccelerationZ, upperByte: true));
206 
207             Registers.Interrupt1Config.Define(this, 0x3F) //RW
208                 .WithFlag(0, out readyEnabledXIrq[0, 0], name: "XL_EVENT_IRQ_ENABLE")
209                 .WithFlag(1, out readyEnabledXIrq[0, 1], name: "XH_EVENT_IRQ_ENABLE")
210                 .WithFlag(2, out readyEnabledYIrq[0, 0], name: "YL_EVENT_IRQ_ENABLE")
211                 .WithFlag(3, out readyEnabledYIrq[0, 1], name: "YH_EVENT_IRQ_ENABLE")
212                 .WithFlag(4, out readyEnabledZIrq[0, 0], name: "ZL_EVENT_IRQ_ENABLE")
213                 .WithFlag(5, out readyEnabledZIrq[0, 1], name: "ZH_EVENT_IRQ_ENABLE")
214                 .WithTaggedFlag("6DIR_DETECT_ENABLE", 6)
215                 .WithTaggedFlag("AO_IRQ_EVENTS", 7)
216                 .WithWriteCallback((_, __) => UpdateInterrupts());
217 
218             Registers.Interrupt1Source.Define(this, 0x40) //RO
219                 .WithFlag(0, out readyPendingXIrq[0, 0], FieldMode.Read, name: "XL_IRQ")
220                 .WithFlag(1, out readyPendingXIrq[0, 1], FieldMode.Read, name: "XH_IRQ")
221                 .WithFlag(2, out readyPendingYIrq[0, 0], FieldMode.Read, name: "YL_IRQ")
222                 .WithFlag(3, out readyPendingYIrq[0, 1], FieldMode.Read, name: "YH_IRQ")
223                 .WithFlag(4, out readyPendingZIrq[0, 0], FieldMode.Read, name: "ZL_IRQ")
224                 .WithFlag(5, out readyPendingZIrq[0, 1], FieldMode.Read, name: "ZH_IRQ")
225                 .WithFlag(6, out activeIrq[0] , FieldMode.Read, name: "IRQ_ACTIVE")
226                 .WithTaggedFlag("PREREQ", 7);
227 
228             Registers.Interrupt2Config.Define(this, 0x3F) //RW
229                 .WithFlag(0, out readyEnabledXIrq[1, 0], name: "XL_EVENT_IRQ_ENABLE")
230                 .WithFlag(1, out readyEnabledXIrq[1, 1], name: "XH_EVENT_IRQ_ENABLE")
231                 .WithFlag(2, out readyEnabledYIrq[1, 0], name: "YL_EVENT_IRQ_ENABLE")
232                 .WithFlag(3, out readyEnabledYIrq[1, 1], name: "YH_EVENT_IRQ_ENABLE")
233                 .WithFlag(4, out readyEnabledZIrq[1, 0], name: "ZL_EVENT_IRQ_ENABLE")
234                 .WithFlag(5, out readyEnabledZIrq[1, 1], name: "ZH_EVENT_IRQ_ENABLE")
235                 .WithTaggedFlag("6DIR_DETECT_ENABLE", 6)
236                 .WithTaggedFlag("AO_IRQ_EVENTS", 7)
237                 .WithWriteCallback((_, __) => UpdateInterrupts());
238 
239             Registers.Interrupt2Source.Define(this, 0x40) //RO
240                 .WithFlag(0, out readyPendingXIrq[1, 0], FieldMode.Read, name: "XL_IRQ")
241                 .WithFlag(1, out readyPendingXIrq[1, 1], FieldMode.Read, name: "XH_IRQ")
242                 .WithFlag(2, out readyPendingYIrq[1, 0], FieldMode.Read, name: "YL_IRQ")
243                 .WithFlag(3, out readyPendingYIrq[1, 1], FieldMode.Read, name: "YH_IRQ")
244                 .WithFlag(4, out readyPendingZIrq[1, 0], FieldMode.Read, name: "ZL_IRQ")
245                 .WithFlag(5, out readyPendingZIrq[1, 1], FieldMode.Read, name: "ZH_IRQ")
246                 .WithFlag(6, out activeIrq[1], FieldMode.Read, name: "IRQ_ACTIVE")
247                 .WithTaggedFlag("PREREQ", 7);
248         }
249 
RegistersAutoIncrement()250         private void RegistersAutoIncrement()
251         {
252             if(multipleOperation)
253             {
254                 registerAddress = (Registers)((int)registerAddress + 1);
255                 this.Log(LogLevel.Noisy, "Auto-incrementing to the next register 0x{0:X} - {0}", registerAddress);
256             }
257         }
258 
GetSensitivity()259         private ushort GetSensitivity()
260         {
261             ushort sensitivity = 0; // [mg/LSB]
262             switch(fullScale.Value)
263             {
264                 case 0:
265                     sensitivity = 1;
266                     break;
267                 case 1:
268                     sensitivity = 2;
269                     break;
270                 case 2:
271                     sensitivity = 4;
272                     break;
273                 case 3:
274                     sensitivity = 12;
275                     break;
276                 default:
277                     this.Log(LogLevel.Warning, "Unsupported value of sensor sensitivity.");
278                     break;
279             }
280             return sensitivity;
281         }
282 
IsAccelerationOutOfRange(decimal acceleration)283         private bool IsAccelerationOutOfRange(decimal acceleration)
284         {
285             // This range protects from the overflow of the short variables in the 'Convert' function.
286             if(acceleration < MinAcceleration || acceleration > MaxAcceleration)
287             {
288                 this.Log(LogLevel.Warning, "Acceleration is out of range, use value from the range <{0};{1}>",
289                                             MinAcceleration, MaxAcceleration);
290                 return true;
291             }
292             return false;
293         }
294 
Convert(decimal value, bool upperByte)295         private byte Convert(decimal value, bool upperByte)
296         {
297             decimal convertedValue = (decimal)(((short)value << 14) / (GetSensitivity() * GravitationalConst));
298             short convertedValueAsShort = (short)convertedValue;
299             return upperByte ? (byte)(convertedValueAsShort >> 8) : (byte)convertedValueAsShort;
300         }
301 
UpdateInterrupts()302         private void UpdateInterrupts()
303         {
304             var statusIrq = false;
305             var statusX = xDataAvailable.Value || xyzDataAvailable.Value;
306             var statusY = yDataAvailable.Value || xyzDataAvailable.Value;
307             var statusZ = yDataAvailable.Value || xyzDataAvailable.Value;
308             for(var irqNo = 0; irqNo < IrqAmount; ++irqNo)
309             {
310                 for(var i = 0; i < AxisBytes; ++i)
311                 {
312                     if(activeIrq[irqNo].Value)
313                     {
314                         readyPendingXIrq[irqNo, i].Value = statusX && readyEnabledXIrq[irqNo, i].Value;
315                         this.Log(LogLevel.Noisy, "Setting readyPendingXIrq[{0}, {1}] to {2}",
316                                 irqNo, i, readyPendingXIrq[irqNo, i].Value);
317 
318                         readyPendingYIrq[irqNo, i].Value = statusY && readyEnabledYIrq[irqNo, i].Value;
319                         this.Log(LogLevel.Noisy, "Setting readyPendingYIrq[{0}, {1}] to {2}",
320                                 irqNo, i, readyPendingYIrq[irqNo, i].Value);
321 
322                         readyPendingZIrq[irqNo, i].Value = statusZ && readyEnabledZIrq[irqNo, i].Value;
323                         this.Log(LogLevel.Noisy, "Setting readyPendingZIrq[{0}, {1}] to {2}",
324                                 irqNo, i, readyPendingZIrq[irqNo, i].Value);
325 
326                         statusIrq = (readyPendingXIrq[irqNo, i].Value ||
327                                       readyPendingYIrq[irqNo, i].Value ||
328                                       readyPendingZIrq[irqNo, i].Value);
329                     }
330                 }
331 
332                 irqs[irqNo].Set(statusIrq);
333                 this.Log(LogLevel.Debug, "Setting IRQ{0} to {1}", irqNo, statusIrq);
334             }
335         }
336 
337         private Registers registerAddress;
338         private bool multipleOperation;
339 
340         private IValueRegisterField dataRate;
341         private IValueRegisterField fullScale;
342         private IFlagRegisterField xyzDataAvailable;
343         private IFlagRegisterField xAxisEnable, yAxisEnable, zAxisEnable;
344         private IFlagRegisterField xDataAvailable, yDataAvailable, zDataAvailable;
345 
346         private GPIO[] irqs = new GPIO[IrqAmount];
347         private IFlagRegisterField[] activeIrq = new IFlagRegisterField[IrqAmount];
348         private IFlagRegisterField[,] readyEnabledXIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
349         private IFlagRegisterField[,] readyEnabledYIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
350         private IFlagRegisterField[,] readyEnabledZIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
351         private IFlagRegisterField[,] readyPendingXIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
352         private IFlagRegisterField[,] readyPendingYIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
353         private IFlagRegisterField[,] readyPendingZIrq = new IFlagRegisterField[IrqAmount, AxisBytes];
354 
355         private decimal accelarationX;
356         private decimal accelarationY;
357         private decimal accelarationZ;
358 
359         private const decimal MinAcceleration = -19.0m;
360         private const decimal MaxAcceleration = 19.0m;
361         private const decimal GravitationalConst = 9.806650m; // [m/s^2]
362         private const ushort AxisBytes = 2;
363         private const ushort IrqAmount = 2;
364 
365         private enum Registers : byte
366         {
367             ChipID = 0x0F,
368             // Reserved: 0x00 - 0x1F
369             Control1 = 0x20,
370             Control4 = 0x23,
371             StatusReg = 0x27,
372             DataOutXL = 0x28,
373             DataOutXH = 0x29,
374             DataOutYL = 0x2A,
375             DataOutYH = 0x2B,
376             DataOutZL = 0x2C,
377             DataOutZH = 0x2D,
378             Interrupt1Config = 0x30,
379             Interrupt1Source = 0x31,
380             Interrupt2Config = 0x34,
381             Interrupt2Source = 0x35,
382             // Reserved: 0x3E - 0x3F
383         }
384     }
385 }
386