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