1 // 2 // Copyright (c) 2010-2024 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.Linq; 9 using System.Collections.Generic; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Core.Structure.Registers; 14 using Antmicro.Renode.Peripherals.SPI; 15 using Antmicro.Renode.Utilities; 16 using Antmicro.Renode.Utilities.RESD; 17 18 namespace Antmicro.Renode.Peripherals.Sensors 19 { 20 public class MAX86171 : ISPIPeripheral, IProvidesRegisterCollection<ByteRegisterCollection> 21 { MAX86171(IMachine machine)22 public MAX86171(IMachine machine) 23 { 24 this.machine = machine; 25 this.resdFrequencyMultiplier = 1; 26 27 Interrupt1 = new GPIO(); 28 Interrupt2 = new GPIO(); 29 30 UpdateDefaultMeasurements(); 31 32 circularFifo = new AFESampleFIFO(this); 33 measurementEnabled = new bool[MeasurementRegisterCount]; 34 35 measurementLEDASource = new byte[MeasurementRegisterCount]; 36 measurementLEDBSource = new byte[MeasurementRegisterCount]; 37 measurementLEDCSource = new byte[MeasurementRegisterCount]; 38 39 measurementLEDACurrent = new IValueRegisterField[MeasurementRegisterCount]; 40 measurementLEDBCurrent = new IValueRegisterField[MeasurementRegisterCount]; 41 measurementLEDCCurrent = new IValueRegisterField[MeasurementRegisterCount]; 42 43 measurementPDARange = new uint[MeasurementRegisterCount]; 44 measurementPDBRange = new uint[MeasurementRegisterCount]; 45 46 measurementPDAOffset = new ushort[MeasurementRegisterCount]; 47 measurementPDBOffset = new ushort[MeasurementRegisterCount]; 48 49 ledRange = new IValueRegisterField[MeasurementRegisterCount]; 50 51 RegistersCollection = new ByteRegisterCollection(this, BuildRegisterMap()); 52 } 53 FeedSamplesFromRESD(ReadFilePath filePath, uint channelId = 0, ulong startTimestamp = 0, RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0)54 public void FeedSamplesFromRESD(ReadFilePath filePath, uint channelId = 0, ulong startTimestamp = 0, 55 RESDStreamSampleOffset sampleOffsetType = RESDStreamSampleOffset.Specified, long sampleOffsetTime = 0) 56 { 57 lock(feederThreadLock) 58 { 59 feedingSamplesFromFile = true; 60 feederThread?.Stop(); 61 resdStream?.Dispose(); 62 resdStream = this.CreateRESDStream<MAX86171_AFESample>(filePath, channelId, sampleOffsetType, sampleOffsetTime); 63 resdStream.MetadataChanged += () => 64 { 65 staleConfiguration = true; 66 }; 67 68 // The initial value is set to `RESDFrequencyMultiplier - 1` 69 // in order to load a sample at the first callback instead of waiting for `RESDFrequencyMultiplier` of them 70 var count = RESDFrequencyMultiplier - 1; 71 feederThread = resdStream.StartSampleFeedThread(this, RESDFrequencyMultiplier * CalculateCurrentFrequency(), (sample, ts, status) => 72 { 73 count++; 74 75 if(status == RESDStreamStatus.AfterStream) 76 { 77 feedingSamplesFromFile = false; 78 TryFeedDefaultSample(); 79 return; 80 } 81 82 if(count != RESDFrequencyMultiplier) 83 { 84 return; 85 } 86 count = 0; 87 88 if(status == RESDStreamStatus.OK) 89 { 90 circularFifo.EnqueueFrame(new AFESampleFrame(sample.Frame.Select(v => new AFESample(v)).ToArray())); 91 } 92 else 93 { 94 circularFifo.EnqueueFrame(defaultMeasurements); 95 } 96 }, startTime: startTimestamp); 97 this.Log(LogLevel.Info, "Started feeding samples from RESD file at {0}Hz", CalculateCurrentFrequency()); 98 } 99 } 100 WriteByte(long offset, byte value)101 public void WriteByte(long offset, byte value) 102 { 103 RegistersCollection.Write(offset, value); 104 } 105 ReadByte(long offset)106 public byte ReadByte(long offset) 107 { 108 return RegistersCollection.Read(offset); 109 } 110 FinishTransmission()111 public void FinishTransmission() 112 { 113 chosenRegister = null; 114 state = null; 115 } 116 Reset()117 public void Reset() 118 { 119 RegistersCollection.Reset(); 120 chosenRegister = null; 121 state = null; 122 circularFifo.Reset(); 123 previousFifoTresholdReached = false; 124 staleConfiguration = true; 125 126 for(var i = 0; i < MeasurementRegisterCount; ++i) 127 { 128 measurementEnabled[i] = false; 129 130 measurementLEDASource[i] = 0; 131 measurementLEDBSource[i] = 0; 132 measurementLEDCSource[i] = 0; 133 134 measurementPDARange[i] = 0; 135 measurementPDBRange[i] = 0; 136 137 measurementPDAOffset[i] = 0; 138 measurementPDBOffset[i] = 0; 139 } 140 UpdateInterrupts(); 141 142 feederThread?.Stop(); 143 feederThread = null; 144 feedingSamplesFromFile = false; 145 } 146 Transmit(byte data)147 public byte Transmit(byte data) 148 { 149 byte output = 0x00; 150 if(chosenRegister == null) 151 { 152 // In first byte, we are choosing register to read or write 153 chosenRegister = (Registers)data; 154 return output; 155 } 156 157 if(state == null) 158 { 159 // In second byte, we are choosing transaction type 160 state = (States)data; 161 return output; 162 } 163 164 switch(state.Value) 165 { 166 case States.Write: 167 WriteByte((long)chosenRegister.Value, data); 168 break; 169 case States.Read: 170 output = ReadByte((long)chosenRegister.Value); 171 break; 172 default: 173 throw new Exception("unreachable code"); 174 } 175 176 return output; 177 } 178 179 public ByteRegisterCollection RegistersCollection { get; } 180 181 public GPIO Interrupt1 { get; } 182 public GPIO Interrupt2 { get; } 183 184 public int Measurement1ADCValue 185 { 186 get => measurement1ADCValue; 187 set 188 { 189 measurement1ADCValue = value; 190 UpdateDefaultMeasurements(); 191 } 192 } 193 194 public int Measurement2ADCValue 195 { 196 get => measurement2ADCValue; 197 set 198 { 199 measurement2ADCValue = value; 200 UpdateDefaultMeasurements(); 201 } 202 } 203 204 public int Measurement3ADCValue 205 { 206 get => measurement3ADCValue; 207 set 208 { 209 measurement3ADCValue = value; 210 UpdateDefaultMeasurements(); 211 } 212 } 213 214 public int Measurement4ADCValue 215 { 216 get => measurement4ADCValue; 217 set 218 { 219 measurement4ADCValue = value; 220 UpdateDefaultMeasurements(); 221 } 222 } 223 224 public int Measurement5ADCValue 225 { 226 get => measurement5ADCValue; 227 set 228 { 229 measurement5ADCValue = value; 230 UpdateDefaultMeasurements(); 231 } 232 } 233 234 public int Measurement6ADCValue 235 { 236 get => measurement6ADCValue; 237 set 238 { 239 measurement6ADCValue = value; 240 UpdateDefaultMeasurements(); 241 } 242 } 243 244 public int Measurement7ADCValue 245 { 246 get => measurement7ADCValue; 247 set 248 { 249 measurement7ADCValue = value; 250 UpdateDefaultMeasurements(); 251 } 252 } 253 254 public int Measurement8ADCValue 255 { 256 get => measurement8ADCValue; 257 set 258 { 259 measurement8ADCValue = value; 260 UpdateDefaultMeasurements(); 261 } 262 } 263 264 public int Measurement9ADCValue 265 { 266 get => measurement9ADCValue; 267 set 268 { 269 measurement9ADCValue = value; 270 UpdateDefaultMeasurements(); 271 } 272 } 273 274 // This propery allows RESD data to be sampled at N times frequency from the input file, 275 // but still loading them every N'th sample into the FIFO (effectively keeping the initial frequency). 276 // This mechanism may help with synchronization in some specific cases, especially when data from multiple sensors needs to be read precisely. 277 // In general there is no need to change the multiplier from the default value of 1, though. 278 public uint RESDFrequencyMultiplier 279 { 280 get => resdFrequencyMultiplier; 281 set 282 { 283 if(feedingSamplesFromFile) 284 { 285 throw new RecoverableException("Cannot change the RESD frequency multiplier while samples are being fed from a file"); 286 } 287 288 if(value < 1) 289 { 290 throw new RecoverableException($"Attempted to set RESD frequency multiplier to {value}. The multiplier has to be greater or equal to 1"); 291 } 292 293 resdFrequencyMultiplier = value; 294 } 295 } 296 297 public event Action OnFifoFull; 298 UpdateStatus()299 private void UpdateStatus() 300 { 301 statusFifoFull.Value |= FifoThresholdReached && (!fifoAssertThresholdOnce.Value || (previousFifoTresholdReached != FifoThresholdReached)); 302 previousFifoTresholdReached = FifoThresholdReached; 303 304 if(statusFifoFull.Value) 305 { 306 OnFifoFull?.Invoke(); 307 } 308 } 309 UpdateInterrupts()310 private void UpdateInterrupts() 311 { 312 // Currently, only A_FULL interrupt is supported for both INT1 and INT2 313 // GPIO ports. 314 315 var interrupt1 = false; 316 interrupt1 = interrupt1FullEnabled.Value && statusFifoFull.Value;; 317 318 var interrupt2 = false; 319 interrupt2 = interrupt2FullEnabled.Value && statusFifoFull.Value; 320 321 Interrupt1.Set(ApplyInterruptPolarity(interrupt1, polarityInterrupt1.Value)); 322 Interrupt2.Set(ApplyInterruptPolarity(interrupt2, polarityInterrupt2.Value)); 323 } 324 CheckIfConfigurationMatches()325 private bool CheckIfConfigurationMatches() 326 { 327 if(!staleConfiguration) 328 { 329 return true; 330 } 331 332 var currentSample = resdStream?.CurrentSample; 333 if(currentSample == null) 334 { 335 return true; 336 } 337 338 staleConfiguration = false; 339 340 var metadataMatches = true; 341 foreach(var channel in ActiveChannels.Cast<int>()) 342 { 343 var channelId = channel + 1; 344 var nonMatchingMetadata = new List<String>(); 345 if(currentSample.ConfigLedAExposure[channelId].HasValue && CalculateExposure((uint)measurementLEDACurrent[channel].Value, (uint)ledRange[channel].Value, out var ledAExposure) != currentSample.ConfigLedAExposure[channelId]) 346 { 347 nonMatchingMetadata.Add($"LED A exposure (expected: {currentSample.ConfigLedAExposure[channelId]}, got: {ledAExposure})"); 348 } 349 if(currentSample.ConfigLedBExposure[channelId].HasValue && CalculateExposure((uint)measurementLEDBCurrent[channel].Value, (uint)ledRange[channel].Value, out var ledBExposure) != currentSample.ConfigLedBExposure[channelId]) 350 { 351 nonMatchingMetadata.Add($"LED B exposure (expected: {currentSample.ConfigLedBExposure[channelId]}, got: {ledBExposure})"); 352 } 353 if(currentSample.ConfigLedCExposure[channelId].HasValue && CalculateExposure((uint)measurementLEDCCurrent[channel].Value, (uint)ledRange[channel].Value, out var ledCExposure) != currentSample.ConfigLedCExposure[channelId]) 354 { 355 nonMatchingMetadata.Add($"LED C exposure (expected: {currentSample.ConfigLedCExposure[channelId]}, got: {ledCExposure})"); 356 } 357 if(currentSample.ConfigLedASource[channelId].HasValue && measurementLEDASource[channel] != currentSample.ConfigLedASource[channelId]) 358 { 359 nonMatchingMetadata.Add($"LED A source (expected: {measurementLEDASource[channel]}, got: {currentSample.ConfigLedASource[channelId]})"); 360 } 361 if(currentSample.ConfigLedBSource[channelId].HasValue && measurementLEDBSource[channel] != currentSample.ConfigLedBSource[channelId]) 362 { 363 nonMatchingMetadata.Add($"LED B source (expected: {measurementLEDBSource[channel]}, got: {currentSample.ConfigLedBSource[channelId]})"); 364 } 365 if(currentSample.ConfigLedCSource[channelId].HasValue && measurementLEDCSource[channel] != currentSample.ConfigLedCSource[channelId]) 366 { 367 nonMatchingMetadata.Add($"LED C source (expected: {measurementLEDCSource[channel]}, got: {currentSample.ConfigLedCSource[channelId]})"); 368 } 369 if(currentSample.ConfigPD1ADCRange[channelId].HasValue && measurementPDARange[channel] != currentSample.ConfigPD1ADCRange[channelId]) 370 { 371 nonMatchingMetadata.Add($"PD A ADC range (expected: {measurementPDARange[channel]}, got: {currentSample.ConfigPD1ADCRange[channel]})"); 372 } 373 if(currentSample.ConfigPD2ADCRange[channelId].HasValue && measurementPDBRange[channel] != currentSample.ConfigPD2ADCRange[channelId]) 374 { 375 nonMatchingMetadata.Add($"PD B ADC range (expected: {measurementPDBRange[channel]}, got: {currentSample.ConfigPD2ADCRange[channelId]})"); 376 } 377 if(currentSample.ConfigPD1DACOffset[channelId].HasValue && measurementPDAOffset[channel] != currentSample.ConfigPD1DACOffset[channelId]) 378 { 379 nonMatchingMetadata.Add($"PD A DAC offset (expected: {measurementPDAOffset[channel]}, got: {currentSample.ConfigPD1DACOffset[channelId]})"); 380 } 381 if(currentSample.ConfigPD2DACOffset[channelId].HasValue && measurementPDBOffset[channel] != currentSample.ConfigPD2DACOffset[channelId]) 382 { 383 nonMatchingMetadata.Add($"PD B DAC offset (expected: {measurementPDBOffset[channel]}, got: {currentSample.ConfigPD2DACOffset[channelId]})"); 384 } 385 386 if(nonMatchingMetadata.Count > 0) 387 { 388 metadataMatches = false; 389 this.Log(LogLevel.Warning, "Measurement {0} has non-matching configuration, found differences in: {1}", 390 channel + 1, 391 string.Join(", ", nonMatchingMetadata)); 392 } 393 } 394 395 return metadataMatches; 396 } 397 CalculateExposure(uint driveCurrent, uint ledRange, out uint exposure)398 private uint CalculateExposure(uint driveCurrent, uint ledRange, out uint exposure) 399 { 400 uint multiplier; 401 switch(ledRange) 402 { 403 case 0: 404 multiplier = 125; 405 break; 406 case 1: 407 multiplier = 250; 408 break; 409 case 2: 410 multiplier = 375; 411 break; 412 case 3: 413 multiplier = 500; 414 break; 415 default: 416 throw new Exception("unreachable code"); 417 } 418 exposure = driveCurrent * multiplier; 419 return exposure; 420 } 421 ApplyInterruptPolarity(bool value, OutputPinPolarity polarity)422 private bool ApplyInterruptPolarity(bool value, OutputPinPolarity polarity) 423 { 424 switch(polarity) 425 { 426 case OutputPinPolarity.OpenDrainActiveLow: 427 case OutputPinPolarity.ActiveLow: 428 return !value; 429 case OutputPinPolarity.ActiveHigh: 430 return value; 431 default: 432 return value; 433 } 434 } 435 GetDefaultValueForSampleSource(SampleSource ss)436 private int GetDefaultValueForSampleSource(SampleSource ss) 437 { 438 switch(ss) 439 { 440 case SampleSource.PPGMeasurement1: 441 return Measurement1ADCValue; 442 case SampleSource.PPGMeasurement2: 443 return Measurement2ADCValue; 444 case SampleSource.PPGMeasurement3: 445 return Measurement3ADCValue; 446 case SampleSource.PPGMeasurement4: 447 return Measurement4ADCValue; 448 case SampleSource.PPGMeasurement5: 449 return Measurement5ADCValue; 450 case SampleSource.PPGMeasurement6: 451 return Measurement6ADCValue; 452 case SampleSource.PPGMeasurement7: 453 return Measurement7ADCValue; 454 case SampleSource.PPGMeasurement8: 455 return Measurement8ADCValue; 456 case SampleSource.PPGMeasurement9: 457 return Measurement9ADCValue; 458 default: 459 this.Log(LogLevel.Warning, "No default value specified for {0}, returning 0"); 460 return 0; 461 } 462 } 463 CreateDummyRegister(string name, byte defaultValue = 0x00, FieldMode fieldMode = FieldMode.Read | FieldMode.Write, bool verbose = true)464 private ByteRegister CreateDummyRegister(string name, byte defaultValue = 0x00, FieldMode fieldMode = FieldMode.Read | FieldMode.Write, bool verbose = true) 465 { 466 // As software could potentially want to check if writes to given register were successful, 467 // we will be using this method to mock unimplemented registers to allow for persistent 468 // writes and reads 469 if(verbose) 470 { 471 return new ByteRegister(this, defaultValue) 472 .WithValueField(0, 8, fieldMode, name: name, 473 valueProviderCallback: value => 474 { 475 this.Log(LogLevel.Warning, "Unhandled read from {0}; returning 0x{1:X02}", name, value); 476 return value; 477 }, 478 writeCallback: (_, value) => 479 { 480 this.Log(LogLevel.Warning, "Unhandled write to {0}; written 0x{1:X02}", name, value); 481 }); 482 } 483 else 484 { 485 return new ByteRegister(this, defaultValue) 486 .WithValueField(0, 8, fieldMode, name: name); 487 } 488 } 489 BuildRegisterMap()490 private Dictionary<long, ByteRegister> BuildRegisterMap() 491 { 492 var registerMap = new Dictionary<long, ByteRegister>() 493 { 494 {(long)Registers.Status1, new ByteRegister(this) 495 .WithFlag(0, out statusFifoFull, FieldMode.ReadToClear, name: "STATUS1.a_full") 496 .WithTaggedFlag("STATUS1.frame_rdy", 1) 497 .WithTaggedFlag("STATUS1.fifo_data_rdy", 2) 498 .WithTaggedFlag("STATUS1.alc_ovf", 3) 499 .WithTaggedFlag("STATUS1.exp_ovf", 4) 500 .WithTaggedFlag("STATUS1.thresh2_hilo", 5) 501 .WithTaggedFlag("STATUS1.thresh1_hilo", 6) 502 .WithTaggedFlag("STATUS1.pwr_rdy", 7) 503 }, 504 // Maked as non-verbose to limit the amount of log messages 505 {(long)Registers.Status2, CreateDummyRegister("STATUS2.data", verbose: false)}, 506 // Maked as non-verbose to limit the amount of log messages 507 {(long)Registers.Status3, CreateDummyRegister("STATUS3.data", verbose: false)}, 508 {(long)Registers.FIFOWritePointer, CreateDummyRegister("FIFO_WR_PTR.data", fieldMode: FieldMode.Read)}, 509 {(long)Registers.FIFOReadPointer, CreateDummyRegister("FIFO_RD_PTR.data", fieldMode: FieldMode.Read)}, 510 {(long)Registers.FIFOCounter1, new ByteRegister(this) 511 .WithValueField(0, 7, FieldMode.Read, name: "FIFO_CNT1.OVF_COUNTER", 512 valueProviderCallback: _ => 0) 513 .WithFlag(7, FieldMode.Read, name: "FIFO_CNT1.FIFO_DATA_COUNT_MSB", 514 valueProviderCallback: _ => (circularFifo.Count & 0x100) != 0) 515 }, 516 {(long)Registers.FIFOCounter2, new ByteRegister(this) 517 .WithValueField(0, 8, FieldMode.Read, name: "FIFO_CNT2.fifo_data_count_lsb", 518 valueProviderCallback: _ => circularFifo.Count) 519 }, 520 {(long)Registers.FIFOData, new ByteRegister(this) 521 .WithValueField(0, 8, FieldMode.Read, name: "FIFO_DATA.data", 522 valueProviderCallback: _ => 523 { 524 CheckIfConfigurationMatches(); 525 var output = circularFifo.DequeueByte(); 526 if(clearFlagsOnRead.Value) 527 { 528 statusFifoFull.Value = false; 529 UpdateInterrupts(); 530 } 531 return output; 532 }) 533 }, 534 {(long)Registers.FIFOConfiguration1, new ByteRegister(this) 535 .WithValueField(0, 8, out fifoFullThreshold, name: "FIFO_CONF1.fifo_a_full") 536 .WithChangeCallback((_, __) => UpdateInterrupts()) 537 }, 538 {(long)Registers.FIFOConfiguration2, new ByteRegister(this) 539 .WithReservedBits(0, 1) 540 .WithFlag(1, name: "FIFO_CONF2.fifo_ro", 541 valueProviderCallback: _ => circularFifo.Rollover, 542 writeCallback: (_, value) => circularFifo.Rollover = value) 543 .WithFlag(2, out fifoAssertThresholdOnce, name: "FIFO_CONF2.a_full_type") 544 .WithFlag(3, out clearFlagsOnRead, name: "FIFO_CONF2.fifo_stat_clr") 545 .WithFlag(4, FieldMode.WriteOneToClear | FieldMode.Read, name: "FIFO_CONF2.flush_fifo", 546 writeCallback: (_, value) => 547 { 548 if(value) 549 { 550 circularFifo.Clear(); 551 552 statusFifoFull.Value = false; 553 UpdateInterrupts(); 554 } 555 }) 556 .WithReservedBits(5, 3) 557 }, 558 {(long)Registers.SystemConfiguration1, new ByteRegister(this) 559 .WithFlag(0, name: "SYSTEM_CONF1.reset", 560 valueProviderCallback: _ => false, 561 writeCallback: (_, value) => { if(value) Reset(); }) 562 .WithFlag(1, name: "SYSTEM_CONF1.shdn", 563 // While completely disabling clocks used for feeding samples to FIFO 564 // would be more in line with what actual software is doing, disabling writes 565 // to FIFO is easier to implement while also being agnostic to samples source 566 valueProviderCallback: _ => !circularFifo.Enabled, 567 writeCallback: (_, value) => circularFifo.Enabled = !value) 568 // Using Flag instead of TaggedFlag for persistancy of data 569 // written by software 570 .WithFlag(2, name: "SYSTEM_CONF1.ppg1_pwrdn") 571 .WithFlag(3, name: "SYSTEM_CONF1.ppg2_pwrdn") 572 .WithValueField(4, 2, name: "SYSTEM_CONF1.sync_mode") 573 .WithFlag(6, name: "SYSTEM_CONF1.sw_force_sync") 574 .WithFlag(7, name: "SYSTEM_CONF1.meas9_en", 575 valueProviderCallback: _ => measurementEnabled[(int)Channel.Measurement9], 576 changeCallback: (_, value) => 577 { 578 measurementEnabled[(int)Channel.Measurement9] = value; 579 TryFeedDefaultSample(); 580 }) 581 }, 582 {(long)Registers.SystemConfiguration2, new ByteRegister(this) 583 .WithFlags(0, 8, name: "SYSTEM_CONF2.MEASX_EN", 584 valueProviderCallback: (idx, _) => measurementEnabled[idx], 585 changeCallback: (idx, _, value) => 586 { 587 measurementEnabled[idx] = value; 588 TryFeedDefaultSample(); 589 }) 590 }, 591 {(long)Registers.SystemConfiguration3, CreateDummyRegister("SYSTEM_CONF3.data")}, 592 {(long)Registers.PhotodiodeBias, CreateDummyRegister("PHOTO_BIAS.data")}, 593 {(long)Registers.PinFunctionalConfiguration, CreateDummyRegister("PIN_FUNC_CONF.data")}, 594 {(long)Registers.OutputPinConfiguration, new ByteRegister(this) 595 .WithReservedBits(0, 1) 596 .WithEnumField<ByteRegister, OutputPinPolarity>(1, 2, out polarityInterrupt1, name: "OUT_PIN_CONF.int1_ocfg") 597 .WithEnumField<ByteRegister, OutputPinPolarity>(3, 2, out polarityInterrupt2, name: "OUT_PIN_CONF.int2_ocfg") 598 .WithReservedBits(5, 3) 599 .WithChangeCallback((_, __) => UpdateInterrupts()) 600 }, 601 {(long)Registers.FrameRateClockFrequency, new ByteRegister(this, 0x20) 602 .WithTag("FR_CLK.fine_tune", 0, 5) 603 .WithFlag(5, out clockSelect, name: "FR_CLK.sel") 604 .WithReservedBits(6, 2) 605 .WithChangeCallback((_, __) => UpdateFrequency()) 606 }, 607 {(long)Registers.FrameRateClockDividerMSB, new ByteRegister(this, 0x1) 608 .WithValueField(0, 7, out clockDividerHigh, name: "FR_CLK.div_h") 609 .WithReservedBits(7, 1) 610 .WithChangeCallback((_, __) => UpdateFrequency()) 611 }, 612 {(long)Registers.FrameRateClockDividerLSB, new ByteRegister(this) 613 .WithValueField(0, 8, out clockDividerLow, name: "FR_CLK.div_l") 614 .WithChangeCallback((_, __) => UpdateFrequency()) 615 }, 616 {(long)Registers.ThresholdMeasurementSelect, CreateDummyRegister("THRESH_MEAS_SEL.data")}, 617 {(long)Registers.ThresholdHysteresis, CreateDummyRegister("THRESH_HYST.data")}, 618 {(long)Registers.PPGHiThreshold1, CreateDummyRegister("PPG_HI_THRESH1.data")}, 619 {(long)Registers.PPGLoThreshold1, CreateDummyRegister("PPG_LO_THRESH1.data")}, 620 {(long)Registers.PPGHiThreshold2, CreateDummyRegister("PPG_HI_THRESH2.data")}, 621 {(long)Registers.PPGLoThreshold2, CreateDummyRegister("PPG_LO_THRESH2.data")}, 622 {(long)Registers.PicketFenceMeasurementSelect, CreateDummyRegister("PICKET_FENCE_MEAS_SEL.data")}, 623 {(long)Registers.PicketFenceConfiguration, CreateDummyRegister("PICKET_FENCE_CONF.data")}, 624 {(long)Registers.Interrupt1Enable1, new ByteRegister(this) 625 // Using Flag instead of TaggedFlag for persistancy of data 626 // written by software 627 .WithFlag(0, name: "INT1_ENABLE1.led_tx_en") 628 .WithFlag(1, name: "INT1_ENABLE1.thresh1_hilo_en") 629 .WithFlag(2, name: "INT1_ENABLE1.thresh2_hilo_en") 630 .WithFlag(3, name: "INT1_ENABLE1.exp_ovf_en") 631 .WithFlag(4, name: "INT1_ENABLE1.alc_ovf_en") 632 .WithFlag(5, name: "INT1_ENABLE1.fifo_data_rdy_en") 633 .WithFlag(6, name: "INT1_ENABLE1.framerdy_en") 634 .WithFlag(7, out interrupt1FullEnabled, name: "INT1_ENABLE1.a_full_en") 635 .WithChangeCallback((_, __) => UpdateInterrupts()) 636 }, 637 {(long)Registers.Interrupt1Enable2, CreateDummyRegister("INT1_ENABLE2.data")}, 638 {(long)Registers.Interrupt1Enable3, CreateDummyRegister("INT1_ENABLE3.data")}, 639 {(long)Registers.Interrupt2Enable1, new ByteRegister(this) 640 // Using Flag instead of TaggedFlag for persistancy of data 641 // written by software 642 .WithFlag(0, name: "INT2_ENABLE1.led_tx_en") 643 .WithFlag(1, name: "INT2_ENABLE1.thresh1_hilo_en") 644 .WithFlag(2, name: "INT2_ENABLE1.thresh2_hilo_en") 645 .WithFlag(3, name: "INT2_ENABLE1.exp_ovf_en") 646 .WithFlag(4, name: "INT2_ENABLE1.alc_ovf_en") 647 .WithFlag(5, name: "INT2_ENABLE1.fifo_data_rdy_en") 648 .WithFlag(6, name: "INT2_ENABLE1.framerdy_en") 649 .WithFlag(7, out interrupt2FullEnabled, name: "INT2_ENABLE1.a_full_en") 650 .WithChangeCallback((_, __) => UpdateInterrupts()) 651 }, 652 {(long)Registers.Interrupt2Enable2, CreateDummyRegister("INT2_ENABLE2.data")}, 653 {(long)Registers.Interrupt2Enable3, CreateDummyRegister("INT2_ENABLE3.data")}, 654 {(long)Registers.PartID, new ByteRegister(this) 655 .WithValueField(0, 8, FieldMode.Read, name: "PART_ID.part_id", 656 valueProviderCallback: _ => 0x2C) 657 } 658 }; 659 660 for(var i = 0; i < MeasurementRegisterCount; ++i) 661 { 662 var offset = i * 0x8; 663 var j = i; 664 665 registerMap.Add((long)Registers.Measurement1Select + offset, new ByteRegister(this) 666 .WithValueField(0, 2, name: $"MEAS{i+1}_SELECTS.meas{i+1}_drva", 667 writeCallback: (_, value) => 668 { 669 // Map register value to LED source index 670 switch(value) 671 { 672 case 0: 673 measurementLEDASource[j] = 1; 674 break; 675 case 1: 676 measurementLEDASource[j] = 2; 677 break; 678 case 2: 679 measurementLEDASource[j] = 4; 680 break; 681 case 3: 682 measurementLEDASource[j] = 7; 683 break; 684 default: 685 throw new Exception("unreachable code"); 686 } 687 }) 688 .WithValueField(2, 2, name: $"MEAS{i+1}_SELECTS.meas{i+1}_drvb", 689 writeCallback: (_, value) => 690 { 691 // Map register value to LED source index 692 switch(value) 693 { 694 case 0: 695 measurementLEDBSource[j] = 2; 696 break; 697 case 1: 698 measurementLEDBSource[j] = 3; 699 break; 700 case 2: 701 measurementLEDBSource[j] = 5; 702 break; 703 case 3: 704 measurementLEDBSource[j] = 8; 705 break; 706 default: 707 throw new Exception("unreachable code"); 708 } 709 }) 710 .WithValueField(4, 2, name: $"MEAS{i+1}_SELECTS.meas{i+1}_drvc", 711 writeCallback: (_, value) => 712 { 713 // Map register value to LED source index 714 switch(value) 715 { 716 case 0: 717 measurementLEDCSource[j] = 1; 718 break; 719 case 1: 720 measurementLEDCSource[j] = 3; 721 break; 722 case 2: 723 measurementLEDCSource[j] = 6; 724 break; 725 case 3: 726 measurementLEDCSource[j] = 9; 727 break; 728 default: 729 throw new Exception("unreachable code"); 730 } 731 }) 732 .WithFlag(6, name: $"MEAS{i+1}_SELECTS.meas{i+1}_amb") 733 .WithReservedBits(7, 1) 734 .WithChangeCallback((_, __) => staleConfiguration = true)); 735 registerMap.Add((long)Registers.Measurement1Configuration1 + offset, CreateDummyRegister($"MEAS{i+1}_CONF1.data")); 736 registerMap.Add((long)Registers.Measurement1Configuration2 + offset, new ByteRegister(this, 0x3A) 737 .WithValueField(0, 2, name: $"MEAS{i+1}_CONF2.meas{i+1}_ppg1_adc_rge", 738 writeCallback: (_, value) => measurementPDARange[j] = (uint)(4 << (int)value)) 739 .WithValueField(2, 2, name: $"MEAS{i+1}_CONF2.meas{i+1}_ppg2_adc_rge", 740 writeCallback: (_, value) => measurementPDBRange[j] = (uint)(4 << (int)value)) 741 .WithValueField(4, 2, out ledRange[i], name: $"MEAS{i+1}_CONF2.meas{i+1}_led_rge") 742 .WithFlag(6, name: $"MEAS{i+1}_CONF2.meas{i+1}_filt_sel") 743 .WithFlag(7, name: $"MEAS{i+1}_CONF2.meas{i+1}_sinc3_sel") 744 .WithChangeCallback((_, __) => staleConfiguration = true)); 745 registerMap.Add((long)Registers.Measurement1Configuration3 + offset, new ByteRegister(this, 0x50) 746 .WithValueField(0, 2, name: $"MEAS{i+1}_CONF3.meas{i+1}_ppg1_dacoff", 747 writeCallback: (_, value) => measurementPDAOffset[j] = (ushort)(8 * value)) 748 .WithValueField(2, 2, name: $"MEAS{i+1}_CONF3.meas{i+1}_ppg2_dacoff", 749 writeCallback: (_, value) => measurementPDBOffset[j] = (ushort)(8 * value)) 750 .WithValueField(4, 2, name: $"MEAS{i+1}_CONF3.meas{i+1}_led_setlng") 751 .WithValueField(6, 2, name: $"MEAS{i+1}_CONF3.meas{i+1}_pd_setlng") 752 .WithChangeCallback((_, __) => staleConfiguration = true)); 753 registerMap.Add((long)Registers.Measurement1DriverACurrent + offset, new ByteRegister(this) 754 .WithValueField(0, 8, out measurementLEDACurrent[i], name: $"MEAS{i+1}_DRVA_CURRENT.data") 755 .WithChangeCallback((_, __) => staleConfiguration = true)); 756 registerMap.Add((long)Registers.Measurement1DriverBCurrent + offset, new ByteRegister(this) 757 .WithValueField(0, 8, out measurementLEDBCurrent[i], name: $"MEAS{i+1}_DRVB_CURRENT.data") 758 .WithChangeCallback((_, __) => staleConfiguration = true)); 759 registerMap.Add((long)Registers.Measurement1DriverCCurrent + offset, new ByteRegister(this) 760 .WithValueField(0, 8, out measurementLEDCCurrent[i], name: $"MEAS{i+1}_DRVC_CURRENT.data") 761 .WithChangeCallback((_, __) => staleConfiguration = true)); 762 } 763 764 return registerMap; 765 } 766 TryFeedDefaultSample()767 private bool TryFeedDefaultSample() 768 { 769 lock(feederThreadLock) 770 { 771 if(feedingSamplesFromFile) 772 { 773 return false; 774 } 775 776 feederThread?.Stop(); 777 if(measurementEnabled.Any(x => x)) 778 { 779 var freq = CalculateCurrentFrequency(); 780 this.Log(LogLevel.Info, "Starting the default sample feeding at {0}Hz", freq); 781 782 Action feedSample = () => 783 { 784 circularFifo.EnqueueFrame(defaultMeasurements); 785 }; 786 787 Func<bool> stopCondition = () => 788 { 789 return feedingSamplesFromFile; 790 }; 791 792 feederThread = machine.ObtainManagedThread(feedSample, freq, "default_sample_afe", this, stopCondition); 793 feederThread.Start(); 794 795 return true; 796 } 797 798 return false; 799 } 800 } 801 UpdateDefaultMeasurements()802 private void UpdateDefaultMeasurements() 803 { 804 var ch1 = new AFESample(SampleSource.PPGMeasurement1, Measurement1ADCValue); 805 var ch2 = new AFESample(SampleSource.PPGMeasurement2, Measurement2ADCValue); 806 var ch3 = new AFESample(SampleSource.PPGMeasurement3, Measurement3ADCValue); 807 var ch4 = new AFESample(SampleSource.PPGMeasurement4, Measurement4ADCValue); 808 var ch5 = new AFESample(SampleSource.PPGMeasurement5, Measurement5ADCValue); 809 var ch6 = new AFESample(SampleSource.PPGMeasurement6, Measurement6ADCValue); 810 var ch7 = new AFESample(SampleSource.PPGMeasurement7, Measurement7ADCValue); 811 var ch8 = new AFESample(SampleSource.PPGMeasurement8, Measurement8ADCValue); 812 var ch9 = new AFESample(SampleSource.PPGMeasurement9, Measurement9ADCValue); 813 814 // fifo requires two samples per channel 815 defaultMeasurements = new AFESampleFrame(new [] 816 { 817 ch1, ch1, 818 ch2, ch2, 819 ch3, ch3, 820 ch4, ch4, 821 ch5, ch5, 822 ch6, ch6, 823 ch7, ch7, 824 ch8, ch8, 825 ch9, ch9 826 }); 827 } 828 UpdateFrequency()829 private void UpdateFrequency() 830 { 831 if(feederThread != null) 832 { 833 feederThread.Frequency = CalculateCurrentFrequency(); 834 } 835 } 836 CalculateCurrentFrequency()837 public uint CalculateCurrentFrequency() 838 { 839 var clockBaseFrequency = clockSelect.Value 840 ? 32768u 841 : 32000u; 842 return (uint)(clockBaseFrequency / ((clockDividerHigh.Value << 8) + clockDividerLow.Value)); 843 } 844 845 private IEnumerable<Channel> ActiveChannels => 846 measurementEnabled.Select((active, idx) => active ? idx : -1).Where(x => x != -1).Select(x => (Channel)x); 847 848 private bool FifoThresholdReached => 849 (MaximumFIFOCount - circularFifo.Count) <= fifoFullThreshold.Value; 850 851 private readonly IMachine machine; 852 private readonly AFESampleFIFO circularFifo; 853 private readonly bool[] measurementEnabled; 854 private readonly object feederThreadLock = new object(); 855 856 private readonly byte[] measurementLEDASource; 857 private readonly byte[] measurementLEDBSource; 858 private readonly byte[] measurementLEDCSource; 859 860 private readonly uint[] measurementPDARange; 861 private readonly uint[] measurementPDBRange; 862 863 private readonly ushort[] measurementPDAOffset; 864 private readonly ushort[] measurementPDBOffset; 865 866 private bool feedingSamplesFromFile; 867 private bool staleConfiguration; 868 869 private int measurement1ADCValue; 870 private int measurement2ADCValue; 871 private int measurement3ADCValue; 872 private int measurement4ADCValue; 873 private int measurement5ADCValue; 874 private int measurement6ADCValue; 875 private int measurement7ADCValue; 876 private int measurement8ADCValue; 877 private int measurement9ADCValue; 878 879 private uint resdFrequencyMultiplier; 880 881 private AFESampleFrame defaultMeasurements; 882 private IManagedThread feederThread; 883 private RESDStream<MAX86171_AFESample> resdStream; 884 private States? state; 885 private Registers? chosenRegister; 886 private bool previousFifoTresholdReached; 887 888 private IFlagRegisterField statusFifoFull; 889 890 private IFlagRegisterField clockSelect; 891 private IValueRegisterField clockDividerHigh; 892 private IValueRegisterField clockDividerLow; 893 894 private IValueRegisterField[] measurementLEDACurrent; 895 private IValueRegisterField[] measurementLEDBCurrent; 896 private IValueRegisterField[] measurementLEDCCurrent; 897 898 private IValueRegisterField[] ledRange; 899 900 private IFlagRegisterField interrupt1FullEnabled; 901 private IFlagRegisterField interrupt2FullEnabled; 902 903 private IFlagRegisterField fifoAssertThresholdOnce; 904 private IFlagRegisterField clearFlagsOnRead; 905 906 private IValueRegisterField fifoFullThreshold; 907 908 private IEnumRegisterField<OutputPinPolarity> polarityInterrupt1; 909 private IEnumRegisterField<OutputPinPolarity> polarityInterrupt2; 910 911 private const int MeasurementRegisterCount = 9; 912 private const int MaximumFIFOCount = 256; 913 914 [SampleType(SampleType.Custom)] 915 private class MAX86171_AFESample : RESDSample 916 { 917 public override int? Width => null; 918 919 public int[] Frame { get; private set; } 920 921 public ushort?[] ConfigLedAExposure => Enumerable.Range(0, MaxChannels).Select(i => 922 Metadata.TryGetValue($"meas{i}_led_a_exposure", out var value) ? value.As<ushort?>() : null 923 ).ToArray(); 924 925 public byte?[] ConfigLedASource => Enumerable.Range(0, MaxChannels).Select(i => 926 Metadata.TryGetValue($"meas{i}_led_a_source", out var value) ? value.As<byte?>() : null 927 ).ToArray(); 928 929 public ushort?[] ConfigLedBExposure => Enumerable.Range(0, MaxChannels).Select(i => 930 Metadata.TryGetValue($"meas{i}_led_b_exposure", out var value) ? value.As<ushort?>() : null 931 ).ToArray(); 932 933 public byte?[] ConfigLedBSource => Enumerable.Range(0, MaxChannels).Select(i => 934 Metadata.TryGetValue($"meas{i}_led_b_source", out var value) ? value.As<byte?>() : null 935 ).ToArray(); 936 937 public ushort?[] ConfigLedCExposure => Enumerable.Range(0, MaxChannels).Select(i => 938 Metadata.TryGetValue($"meas{i}_led_c_exposure", out var value) ? value.As<ushort?>() : null 939 ).ToArray(); 940 941 public byte?[] ConfigLedCSource => Enumerable.Range(0, MaxChannels).Select(i => 942 Metadata.TryGetValue($"meas{i}_led_c_source", out var value) ? value.As<byte?>() : null 943 ).ToArray(); 944 945 public byte?[] ConfigPD1SourceFlags => Enumerable.Range(0, MaxChannels).Select(i => 946 Metadata.TryGetValue($"meas{i}_pd_a_source_flags", out var value) ? value.As<byte?>() : null 947 ).ToArray(); 948 949 public uint?[] ConfigPD1ADCRange => Enumerable.Range(0, MaxChannels).Select(i => 950 Metadata.TryGetValue($"meas{i}_pd_a_adc_range", out var value) ? value.As<uint?>() : null 951 ).ToArray(); 952 953 public short?[] ConfigPD1DACOffset => Enumerable.Range(0, MaxChannels).Select(i => 954 Metadata.TryGetValue($"meas{i}_pd_a_dac_offset", out var value) ? value.As<short?>() : null 955 ).ToArray(); 956 957 public byte?[] ConfigPD2SourceFlags => Enumerable.Range(0, MaxChannels).Select(i => 958 Metadata.TryGetValue($"meas{i}_pd_b_source_flags", out var value) ? value.As<byte?>() : null 959 ).ToArray(); 960 961 public uint?[] ConfigPD2ADCRange => Enumerable.Range(0, MaxChannels).Select(i => 962 Metadata.TryGetValue($"meas{i}_pd_b_adc_range", out var value) ? value.As<uint?>() : null 963 ).ToArray(); 964 965 public short?[] ConfigPD2DACOffset => Enumerable.Range(0, MaxChannels).Select(i => 966 Metadata.TryGetValue($"meas{i}_pd_b_dac_offset", out var value) ? value.As<short?>() : null 967 ).ToArray(); 968 TryReadFromStream(SafeBinaryReader reader)969 public override bool TryReadFromStream(SafeBinaryReader reader) 970 { 971 var frameLength = reader.ReadByte(); 972 var currentFrame = new int[frameLength]; 973 974 for(var i = 0; i < frameLength; ++i) 975 { 976 currentFrame[i] = reader.ReadInt32(); 977 } 978 979 Frame = currentFrame; 980 return true; 981 } 982 Skip(SafeBinaryReader reader, int count)983 public override bool Skip(SafeBinaryReader reader, int count) 984 { 985 for(; count > 0 && !reader.EOF; count--) 986 { 987 var frameLength = reader.ReadByte(); 988 reader.SkipBytes(frameLength * 4); 989 } 990 return count == 0; 991 } 992 ToString()993 public override string ToString() 994 { 995 var sampleFrame = Frame.Select(sample => new AFESample(sample)); 996 return String.Join(", ", sampleFrame.Select(frame => $"[{frame}]")); 997 } 998 999 private const int MaxChannels = 9; 1000 } 1001 1002 private class AFESampleFIFO 1003 { AFESampleFIFO(MAX86171 parent)1004 public AFESampleFIFO(MAX86171 parent) 1005 { 1006 samples = new Queue<AFESample>(); 1007 this.parent = parent; 1008 Reset(); 1009 } 1010 EnqueueFrame(AFESampleFrame frame)1011 public void EnqueueFrame(AFESampleFrame frame) 1012 { 1013 var activeChannels = parent.ActiveChannels.ToList(); 1014 var channelsInData = new List<Channel>(); 1015 foreach(var samplePair in frame.SamplePairs) 1016 { 1017 var channel = SampleSourceToChannel(samplePair[0].Tag); 1018 if(!channel.HasValue || !activeChannels.Contains(channel.Value)) 1019 { 1020 continue; 1021 } 1022 channelsInData.Add(channel.Value); 1023 1024 foreach(var sample in samplePair) 1025 { 1026 parent.circularFifo.EnqueueSample(sample); 1027 } 1028 } 1029 1030 var missingChannels = activeChannels.Except(channelsInData).ToList(); 1031 if(missingChannels.Count > 0) 1032 { 1033 parent.Log(LogLevel.Warning, "Provided sample data is missing samples for {0} channels: {1}", 1034 missingChannels.Count, 1035 string.Join(", ", missingChannels.Select(chan => chan.ToString()))); 1036 } 1037 1038 parent.UpdateStatus(); 1039 parent.UpdateInterrupts(); 1040 } 1041 EnqueueSample(AFESample sample)1042 public void EnqueueSample(AFESample sample) 1043 { 1044 if(!Enabled) 1045 { 1046 return; 1047 } 1048 1049 if(samples.Count == MaximumFIFOCount) 1050 { 1051 parent.Log(LogLevel.Warning, "Sample FIFO overrun"); 1052 if(Rollover) 1053 { 1054 samples.Dequeue(); 1055 } 1056 else 1057 { 1058 return; 1059 } 1060 } 1061 samples.Enqueue(sample); 1062 } 1063 DequeueByte()1064 public byte DequeueByte() 1065 { 1066 byte output = default(byte); 1067 if(currentSampleEnumerator == null || !currentSampleEnumerator.TryGetNext(out output)) 1068 { 1069 if(samples.TryDequeue(out var currentSample)) 1070 { 1071 currentSampleEnumerator = currentSample.Enumerator; 1072 currentSampleEnumerator.TryGetNext(out output); 1073 } 1074 else 1075 { 1076 return (byte)SampleSource.InvalidData; 1077 } 1078 } 1079 // output variable is always set to proper value 1080 return output; 1081 } 1082 Clear()1083 public void Clear() 1084 { 1085 samples.Clear(); 1086 } 1087 Reset()1088 public void Reset() 1089 { 1090 Enabled = true; 1091 Rollover = false; 1092 Clear(); 1093 } 1094 SampleSourceToChannel(SampleSource ss)1095 private Channel? SampleSourceToChannel(SampleSource ss) 1096 { 1097 switch(ss) 1098 { 1099 case SampleSource.PPGMeasurement1: 1100 return Channel.Measurement1; 1101 case SampleSource.PPGMeasurement2: 1102 return Channel.Measurement2; 1103 case SampleSource.PPGMeasurement3: 1104 return Channel.Measurement3; 1105 case SampleSource.PPGMeasurement4: 1106 return Channel.Measurement4; 1107 case SampleSource.PPGMeasurement5: 1108 return Channel.Measurement5; 1109 case SampleSource.PPGMeasurement6: 1110 return Channel.Measurement6; 1111 case SampleSource.PPGMeasurement7: 1112 return Channel.Measurement7; 1113 case SampleSource.PPGMeasurement8: 1114 return Channel.Measurement8; 1115 case SampleSource.PPGMeasurement9: 1116 return Channel.Measurement9; 1117 default: 1118 return null; 1119 } 1120 } 1121 1122 public bool Enabled { get; set; } 1123 public bool Rollover { get; set; } 1124 public uint Count => (uint)samples.Count; 1125 1126 private IEnumerator<byte> currentSampleEnumerator; 1127 1128 private readonly Queue<AFESample> samples; 1129 private readonly MAX86171 parent; 1130 } 1131 1132 private struct AFESample 1133 { AFESampleAntmicro.Renode.Peripherals.Sensors.MAX86171.AFESample1134 public AFESample(decimal packet) 1135 { 1136 innerPacket = (int)packet; 1137 } 1138 AFESampleAntmicro.Renode.Peripherals.Sensors.MAX86171.AFESample1139 public AFESample(SampleSource tag, int value) 1140 { 1141 innerPacket = (((int)tag & 0xF) << 20) | (value & 0x0FFFFF); 1142 } 1143 1144 public int Value => ((innerPacket & 0x0FFFFF) << 12) >> 12; 1145 public SampleSource Tag => (SampleSource)((innerPacket & 0xF00000) >> 20); 1146 public byte Byte1 => (byte)((innerPacket & 0xFF0000) >> 16); 1147 public byte Byte2 => (byte)((innerPacket & 0x00FF00) >> 8); 1148 public byte Byte3 => (byte)(innerPacket & 0x0000FF); 1149 public byte[] Bytes => new byte[] { Byte1, Byte2, Byte3 }; 1150 public IEnumerator<byte> Enumerator => Bytes.OfType<byte>().GetEnumerator(); 1151 ToStringAntmicro.Renode.Peripherals.Sensors.MAX86171.AFESample1152 public override string ToString() 1153 { 1154 return $"{Tag}: {Value}"; 1155 } 1156 1157 private readonly int innerPacket; 1158 } 1159 1160 private class AFESampleFrame 1161 { AFESampleFrame(AFESample[] samples)1162 public AFESampleFrame(AFESample[] samples) 1163 { 1164 this.samples = samples; 1165 } 1166 1167 public IEnumerable<AFESample[]> SamplePairs 1168 { 1169 get 1170 { 1171 var i = 0; 1172 while(i < samples.Length) 1173 { 1174 if((i + 1) >= samples.Length || samples[i].Tag != samples[i + 1].Tag) 1175 { 1176 Logger.Log(LogLevel.Warning, "Missing second sample for {0}", samples[i].Tag); 1177 i += 1; 1178 continue; 1179 } 1180 1181 if(i > 0 && samples[i - 1].Tag > samples[i].Tag) 1182 { 1183 Logger.Log(LogLevel.Warning, "Invalid order of samples: {0} is before {1}", samples[i - 1].Tag, samples[i].Tag); 1184 } 1185 1186 yield return new AFESample[2] { samples[i], samples[i + 1] }; 1187 i += 2; 1188 } 1189 } 1190 } 1191 1192 private AFESample[] samples; 1193 } 1194 1195 private enum SampleSource : byte 1196 { 1197 Reserved1, 1198 PPGMeasurement1, 1199 PPGMeasurement2, 1200 PPGMeasurement3, 1201 PPGMeasurement4, 1202 PPGMeasurement5, 1203 PPGMeasurement6, 1204 PPGMeasurement7, 1205 PPGMeasurement8, 1206 PPGMeasurement9, 1207 PPGDarkData, 1208 PPGALCOverflow, 1209 PPGExposureOverflow, 1210 PPGPicketFenceData, 1211 InvalidData, 1212 Reaserved2, 1213 } 1214 1215 private enum Channel 1216 { 1217 Measurement1, 1218 Measurement2, 1219 Measurement3, 1220 Measurement4, 1221 Measurement5, 1222 Measurement6, 1223 Measurement7, 1224 Measurement8, 1225 Measurement9, 1226 } 1227 1228 private enum OutputPinPolarity 1229 { 1230 OpenDrainActiveLow = 0, 1231 ActiveHigh = 1, 1232 ActiveLow = 2, 1233 NotDefined = 3, 1234 } 1235 1236 private enum States : byte 1237 { 1238 Write = 0x00, 1239 Read = 0x80, 1240 } 1241 1242 private enum Registers : byte 1243 { 1244 Status1 = 0x00, 1245 Status2, 1246 Status3, 1247 1248 FIFOWritePointer = 0x04, 1249 FIFOReadPointer, 1250 FIFOCounter1, 1251 FIFOCounter2, 1252 FIFOData, 1253 FIFOConfiguration1, 1254 FIFOConfiguration2, 1255 1256 SystemConfiguration1 = 0x0C, 1257 SystemConfiguration2, 1258 SystemConfiguration3, 1259 PhotodiodeBias, 1260 PinFunctionalConfiguration, 1261 OutputPinConfiguration, 1262 1263 FrameRateClockFrequency = 0x15, 1264 FrameRateClockDividerMSB, 1265 FrameRateClockDividerLSB, 1266 1267 Measurement1Select = 0x18, 1268 Measurement1Configuration1, 1269 Measurement1Configuration2, 1270 Measurement1Configuration3, 1271 Measurement1DriverACurrent, 1272 Measurement1DriverBCurrent, 1273 Measurement1DriverCCurrent, 1274 1275 Measurement2Select = 0x20, 1276 Measurement2Configuration1, 1277 Measurement2Configuration2, 1278 Measurement2Configuration3, 1279 Measurement2DriverACurrent, 1280 Measurement2DriverBCurrent, 1281 Measurement2DriverCCurrent, 1282 1283 Measurement3Select = 0x28, 1284 Measurement3Configuration1, 1285 Measurement3Configuration2, 1286 Measurement3Configuration3, 1287 Measurement3DriverACurrent, 1288 Measurement3DriverBCurrent, 1289 Measurement3DriverCCurrent, 1290 1291 Measurement4Select = 0x30, 1292 Measurement4Configuration1, 1293 Measurement4Configuration2, 1294 Measurement4Configuration3, 1295 Measurement4DriverACurrent, 1296 Measurement4DriverBCurrent, 1297 Measurement4DriverCCurrent, 1298 1299 Measurement5Select = 0x38, 1300 Measurement5Configuration1, 1301 Measurement5Configuration2, 1302 Measurement5Configuration3, 1303 Measurement5DriverACurrent, 1304 Measurement5DriverBCurrent, 1305 Measurement5DriverCCurrent, 1306 1307 Measurement6Select = 0x40, 1308 Measurement6Configuration1, 1309 Measurement6Configuration2, 1310 Measurement6Configuration3, 1311 Measurement6DriverACurrent, 1312 Measurement6DriverBCurrent, 1313 Measurement6DriverCCurrent, 1314 1315 Measurement7Select = 0x48, 1316 Measurement7Configuration1, 1317 Measurement7Configuration2, 1318 Measurement7Configuration3, 1319 Measurement7DriverACurrent, 1320 Measurement7DriverBCurrent, 1321 Measurement7DriverCCurrent, 1322 1323 Measurement8Select = 0x50, 1324 Measurement8Configuration1, 1325 Measurement8Configuration2, 1326 Measurement8Configuration3, 1327 Measurement8DriverACurrent, 1328 Measurement8DriverBCurrent, 1329 Measurement8DriverCCurrent, 1330 1331 Measurement9Select = 0x58, 1332 Measurement9Configuration1, 1333 Measurement9Configuration2, 1334 Measurement9Configuration3, 1335 Measurement9DriverACurrent, 1336 Measurement9DriverBCurrent, 1337 Measurement9DriverCCurrent, 1338 1339 ThresholdMeasurementSelect = 0x68, 1340 ThresholdHysteresis, 1341 PPGLoThreshold1, 1342 PPGHiThreshold1, 1343 PPGLoThreshold2, 1344 PPGHiThreshold2, 1345 1346 PicketFenceMeasurementSelect = 0x70, 1347 PicketFenceConfiguration, 1348 1349 Interrupt1Enable1 = 0x78, 1350 Interrupt1Enable2, 1351 Interrupt1Enable3, 1352 Interrupt2Enable1 = 0x7C, 1353 Interrupt2Enable2, 1354 Interrupt2Enable3, 1355 1356 PartID = 0xFF, 1357 } 1358 } 1359 } 1360