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 // uncomment the following line to get warnings about
9 // accessing unhandled registers; it's disabled by default
10 // as it generates a lot of noise in the log and might
11 // hide some important messages
12 //
13 // #define WARN_UNHANDLED_REGISTERS
14 
15 using System;
16 using System.Collections.Generic;
17 using System.IO;
18 using System.Linq;
19 using Antmicro.Renode.Logging;
20 using Antmicro.Renode.Peripherals.I2C;
21 using Antmicro.Renode.Utilities;
22 using Antmicro.Renode.Exceptions;
23 
24 namespace Antmicro.Renode.Peripherals.Sensors
25 {
26     public class ADXL345 : II2CPeripheral
27     {
ADXL345()28         public ADXL345()
29         {
30             samplesFifo = new Queue<Sample>();
31 
32             MaxFifoDepth = 32;
33         }
34 
Reset()35         public void Reset()
36         {
37             range = Range.G2;
38             fullResolution = false;
39             lastRegister = 0;
40             bufferedSamples = null;
41             currentSample = new Sample(0, 0, 0);
42 
43             lock(samplesFifo)
44             {
45                 samplesFifoEmptied = null;
46                 samplesFifo.Clear();
47             }
48         }
49 
FinishTransmission()50         public void FinishTransmission()
51         {
52         }
53 
Write(byte[] data)54         public void Write(byte[] data)
55         {
56             if(data.Length == 0)
57             {
58                 this.Log(LogLevel.Warning, "Unexpected write with no data");
59                 return;
60             }
61 
62             this.Log(LogLevel.Noisy, "Write with {0} bytes of data", data.Length);
63 
64             lastRegister = (Registers)data[0];
65             this.Log(LogLevel.Noisy, "Setting register ID to 0x{0:X} - {0}", lastRegister);
66 
67             if(data.Length > 1)
68             {
69                 this.Log(LogLevel.Noisy, "Handling register write");
70                 // skip the first byte as it contains register address
71                 foreach(var b in data.Skip(1))
72                 {
73                     WriteCurrentRegister(b);
74                     AdvanceRegister();
75                 }
76             }
77             else
78             {
79                 this.Log(LogLevel.Noisy, "Handling register read");
80                 if(lastRegister == Registers.Xdata0)
81                 {
82                     lock(samplesFifo)
83                     {
84                         if(!samplesFifo.TryDequeue(out currentSample))
85                         {
86                             currentSample = new Sample(0, 0, 0);
87                             this.Log(LogLevel.Warning, "Reading from Xdata0 register, but there are no samples");
88                             return;
89                         }
90 
91                         if(samplesFifo.Count == 0 && samplesFifoEmptied != null)
92                         {
93                             samplesFifoEmptied();
94                         }
95                     }
96                 }
97             }
98         }
99 
FeedSample(short x, short y, short z, int repeat = 1)100         public void FeedSample(short x, short y, short z, int repeat = 1)
101         {
102             lock(samplesFifo)
103             {
104                 if(repeat < 0)
105                 {
106                     SamplesFifoEmptied = () => FeedSampleInner(x, y, z);
107                 }
108                 else
109                 {
110                     FeedSampleInner(x, y, z, repeat);
111 
112                     SamplesFifoEmptied = null;
113                 }
114             }
115         }
116 
FeedSample(string path, int repeat = 1)117         public void FeedSample(string path, int repeat = 1)
118         {
119             bufferedSamples = ParseSamplesFile(path);
120 
121             lock(samplesFifo)
122             {
123                 if(repeat < 0)
124                 {
125                     SamplesFifoEmptied = () => FeedSampleInner(bufferedSamples);
126                 }
127                 else
128                 {
129                     FeedSampleInner(bufferedSamples, repeat);
130 
131                     bufferedSamples = null;
132                     SamplesFifoEmptied = null;
133                 }
134             }
135         }
136 
Read(int count = 1)137         public byte[] Read(int count = 1)
138         {
139             this.Log(LogLevel.Noisy, "Reading {0} bytes from register 0x{1:X} - {1}", count, lastRegister);
140 
141             var result = new byte[count];
142             for(var i = 0; i < result.Length; i++)
143             {
144                 result[i] = ReadCurrentRegister();
145                 AdvanceRegister();
146             }
147 
148             this.Log(LogLevel.Noisy, "Read result: {0}", Misc.PrettyPrintCollection(result));
149             return result;
150         }
151 
152         public int MaxFifoDepth { get; set; }
153 
ReadCurrentRegister()154         private byte ReadCurrentRegister()
155         {
156             switch(lastRegister)
157             {
158                 case Registers.DeviceID:
159                     return DevID;
160                 case Registers.Xdata0:
161                     return currentSample.X.GetLowByte(fullResolution, range);
162                 case Registers.Xdata1:
163                     return currentSample.X.GetHighByte(fullResolution, range);
164                 case Registers.Ydata0:
165                     return currentSample.Y.GetLowByte(fullResolution, range);
166                 case Registers.Ydata1:
167                     return currentSample.Y.GetHighByte(fullResolution, range);
168                 case Registers.Zdata0:
169                     return currentSample.Z.GetLowByte(fullResolution, range);
170                 case Registers.Zdata1:
171                     return currentSample.Z.GetHighByte(fullResolution, range);
172                 case Registers.FifoStatus:
173                     return (byte)Math.Min(samplesFifo.Count, MaxFifoDepth);
174 
175                 default:
176 #if WARN_UNHANDLED_REGISTERS
177                     // this generates a lot of noise in the logs so it's disabled by default
178                     this.Log(LogLevel.Warning, "Reading from an unsupported or not-yet-implemented register: 0x{0:X} - {0}", lastRegister);
179 #endif
180                     return 0;
181             }
182         }
183 
WriteCurrentRegister(byte value)184         private void WriteCurrentRegister(byte value)
185         {
186             this.Log(LogLevel.Noisy, "Writing value 0x{0:X} to register {1}", value, lastRegister);
187 
188             switch(lastRegister)
189             {
190                 case Registers.DataFormatControl:
191                     range = (Range)(value & 0x3);
192                     fullResolution = (value >> 3) != 0;
193                     break;
194 
195                 default:
196 #if WARN_UNHANDLED_REGISTERS
197                     // this generates a lot of noise in the logs so it's disabled by default
198                     this.Log(LogLevel.Warning, "Writing to an unsupported or not-yet-implemented register: 0x{0:X} - {0}", lastRegister);
199 #endif
200                     break;
201             }
202         }
203 
AdvanceRegister()204         private void AdvanceRegister()
205         {
206             lastRegister = (Registers)((int)lastRegister + 1);
207             this.Log(LogLevel.Noisy, "Auto-incrementing to the next register 0x{0:X} - {0}", lastRegister);
208         }
209 
ParseSamplesFile(string path)210         private IEnumerable<Sample> ParseSamplesFile(string path)
211         {
212             var localQueue = new Queue<Sample>();
213             var lineNumber = 0;
214 
215             try
216             {
217                 using(var reader = File.OpenText(path))
218                 {
219                     var line = "";
220                     while((line = reader.ReadLine()) != null)
221                     {
222                         ++lineNumber;
223                         var numbers = line.Split(new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Select(a => a.Trim()).ToArray();
224 
225                         if(numbers.Length != 3
226                                 || !short.TryParse(numbers[0], out var x)
227                                 || !short.TryParse(numbers[1], out var y)
228                                 || !short.TryParse(numbers[2], out var z))
229                         {
230                             throw new RecoverableException($"Wrong data file format at line {lineNumber}: {line}");
231                         }
232                         localQueue.Enqueue(new Sample(x, y, z));
233                     }
234                 }
235             }
236             catch(Exception e)
237             {
238                 if(e is RecoverableException)
239                 {
240                     throw;
241                 }
242 
243                 throw new RecoverableException($"There was a problem when reading samples file: {e.Message}");
244             }
245 
246             return localQueue;
247         }
248 
FeedSampleInner(short x, short y, short z, int repeat = 1)249         private void FeedSampleInner(short x, short y, short z, int repeat = 1)
250         {
251             lock(samplesFifo)
252             {
253                 var sample = new Sample(x, y, z);
254                 for(var i = 0; i < repeat; i++)
255                 {
256                     samplesFifo.Enqueue(sample);
257                 }
258             }
259         }
260 
FeedSampleInner(IEnumerable<Sample> samples, int repeat = 1)261         private void FeedSampleInner(IEnumerable<Sample> samples, int repeat = 1)
262         {
263             lock(samplesFifo)
264             {
265                 for(var i = 0; i < repeat; i++)
266                 {
267                     samplesFifo.EnqueueRange(samples);
268                 }
269             }
270         }
271 
272         private Action SamplesFifoEmptied
273         {
274             get => samplesFifoEmptied;
275 
276             set
277             {
278                 lock(samplesFifo)
279                 {
280                     samplesFifoEmptied = value;
281                     if(samplesFifoEmptied != null && samplesFifo.Count == 0)
282                     {
283                         samplesFifoEmptied();
284                     }
285                 }
286             }
287         }
288 
289         private Registers lastRegister;
290         private Range range;
291         private bool fullResolution;
292         private IEnumerable<Sample> bufferedSamples;
293         private Action samplesFifoEmptied;
294         private Sample currentSample;
295 
296         private readonly Queue<Sample> samplesFifo;
297 
298         private const byte DevID = 0xe5;
299 
300         private struct Sample
301         {
SampleAntmicro.Renode.Peripherals.Sensors.ADXL345.Sample302             public Sample(short x, short y, short z)
303             {
304                 X = new SubSample(x);
305                 Y = new SubSample(y);
306                 Z = new SubSample(z);
307             }
308 
ToStringAntmicro.Renode.Peripherals.Sensors.ADXL345.Sample309             public override string ToString()
310             {
311                 return $"[X: {X.RawValue}, Y: {Y.RawValue}, Z: {Z.RawValue}]";
312             }
313 
314             public SubSample X;
315             public SubSample Y;
316             public SubSample Z;
317         }
318 
319         private struct SubSample
320         {
SubSampleAntmicro.Renode.Peripherals.Sensors.ADXL345.SubSample321             public SubSample(short v)
322             {
323                 RawValue = v;
324             }
325 
GetLowByteAntmicro.Renode.Peripherals.Sensors.ADXL345.SubSample326             public byte GetLowByte(bool fullRes, Range range)
327             {
328                 return (byte)(RawValue >> GetShifter(fullRes, range));
329             }
330 
GetHighByteAntmicro.Renode.Peripherals.Sensors.ADXL345.SubSample331             public byte GetHighByte(bool fullRes, Range range)
332             {
333                 return (byte)(RawValue >> (GetShifter(fullRes, range) + 8));
334             }
335 
336             public short RawValue;
337 
GetShifterAntmicro.Renode.Peripherals.Sensors.ADXL345.SubSample338             private int GetShifter(bool fullRes, Range range)
339             {
340                 var shifter = 2;
341 
342                 if(!fullRes)
343                 {
344                     shifter = (int)range + 2;
345                 }
346 
347                 return shifter;
348             }
349         }
350 
351         private enum Range
352         {
353             G2 = 0,
354             G4 = 1,
355             G8 = 2,
356             G16 = 3
357         }
358 
359         private enum Registers : byte
360         {
361             DeviceID = 0x00,
362             // 0x01 to 0x1C are reserved
363             TapThreshold = 0x1D,
364             Xoffset = 0x1E,
365             Yoffset = 0x1F,
366             Zoffset = 0x20,
367             TapDuration = 0x21,
368             TapLatency = 0x22,
369             TapWindow = 0x23,
370             ActivityThreshold = 0x24,
371             InactivityThreshold = 0x25,
372             InactivityTime = 0x26,
373             AxisEnableControlForActivityAndInactivityDetection = 0x27,
374             FreeFallThreshold = 0x28,
375             FreeFallTime = 0x29,
376             AxisControlForSingleTapDoubleTap = 0x2A,
377             SourceOfSingleTapDoubleTap = 0x2B,
378             DataRateAndPowerModeControl = 0x2C,
379             PowerSavingFeaturesControl = 0x2D,
380             InterruptEnableControl = 0x2E,
381             InterruptMappingControl = 0x2F,
382             SourceOfInterrupts = 0x30,
383             DataFormatControl = 0x31,
384             Xdata0 = 0x32,
385             Xdata1 = 0x33,
386             Ydata0 = 0x34,
387             Ydata1 = 0x35,
388             Zdata0 = 0x36,
389             Zdata1 = 0x37,
390             FifoControl = 0x38,
391             FifoStatus = 0x39
392         }
393     }
394 }
395