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 8 using System; 9 using System.Linq; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Core; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Logging; 14 using Antmicro.Renode.Utilities; 15 using Antmicro.Renode.Core.Structure.Registers; 16 using Antmicro.Renode.Peripherals.Bus; 17 using Antmicro.Renode.Peripherals.DMA; 18 using Antmicro.Renode.Peripherals.Sensors; 19 20 namespace Antmicro.Renode.Peripherals.Analog 21 { 22 // Superset of all ADC features found on many STM MPU series. 23 // 24 // Available features: 25 // watchdogCount ------ Specifies the number of analog watchdogs inside the peripheral between 1 and 3. 26 // *hasCalibration ----- Specifies whether the calibration factor and voltage regulator are available to the software. 27 // ADCs without this feature will still have the ADCAL flag available to trigger the calibration procedure, 28 // but not the CALFACT register nor the ADVREGEN field. 29 // channelCount ------- Specifies the amount of available channels. 30 // Includes both internal sources (like the temperature sensor) as well as external. 31 // *hasPrescaler ------- Specifies whether the ADC contains a prescaler for the external clock input. 32 // Technically either this property could be made an enum, 33 // or there could be added a separate property which describes whether the internal clock can be used. 34 // ex. 35 // - the STM32F0xx can either use PCLK or the ADC asynchronous clock and has no precaler 36 // - the STM32WBA only uses the ADC asynchronous clock but has a precaler 37 // but for now, this feature describes both (i.e. true means has prescaler *and* no internal clock). 38 // *hasVbatPin --------- Specifies whether this ADC provides a pin for monitoring of an external power supply. 39 // *hasChannelSequence - Specifies whether this ADC provides a fully configurable sequencer. 40 // If not, the ADC can convert a single channel or a sequence of channels, 41 // but only scanning sequentially either forwards or backwards. 42 // *hasPowerRegister --- Specifies whether this ADC has a separate register for power managment. 43 // If false, that means the model exposes features like auto-off in one of the configuration registers. 44 // *hasChannelSelect --- Specifies whether this ADC has channel selection register. 45 // If false, third watchdog threshold configuration register will live under this register's offset. 46 // 47 // * - Feature is either partially implemented, or not at all. 48 public abstract class STM32_ADC_Common : IKnownSize, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IDoubleWordPeripheral 49 { STM32_ADC_Common(IMachine machine, double referenceVoltage, uint externalEventFrequency, int dmaChannel = 0, IDMA dmaPeripheral = null, int? watchdogCount = null, bool? hasCalibration = null, int? channelCount = null, bool? hasPrescaler = null, bool? hasVbatPin = null, bool? hasChannelSequence = null, bool? hasPowerRegister = null, bool? hasChannelSelect = null)50 public STM32_ADC_Common(IMachine machine, double referenceVoltage, uint externalEventFrequency, int dmaChannel = 0, IDMA dmaPeripheral = null, 51 int? watchdogCount = null, bool? hasCalibration = null, int? channelCount = null, bool? hasPrescaler = null, 52 bool? hasVbatPin = null, bool? hasChannelSequence = null, bool? hasPowerRegister = null, bool? hasChannelSelect = null) 53 { 54 if(!watchdogCount.HasValue || !hasCalibration.HasValue || !channelCount.HasValue || !hasPrescaler.HasValue || 55 !hasVbatPin.HasValue || !hasChannelSequence.HasValue || !hasPowerRegister.HasValue || !hasChannelSelect.HasValue) 56 { 57 throw new ConstructionException("Missing configuration options"); 58 } 59 60 if(dmaPeripheral == null) 61 { 62 if(dmaChannel != 0) 63 { 64 throw new ConstructionException($"Unspecified DMA peripheral to use with channel number {dmaChannel}"); 65 } 66 } 67 else 68 { 69 if(dmaChannel <= 0 || dmaChannel > dmaPeripheral.NumberOfChannels) 70 { 71 throw new ConstructionException($"Invalid 'dmaChannel' argument value: '{dmaChannel}'. Available channels: 1-{dma.NumberOfChannels}"); 72 } 73 } 74 75 this.machine = machine; 76 77 bool calibration = hasCalibration.Value; 78 bool prescaler = hasPrescaler.Value; 79 bool vbatPin = hasVbatPin.Value; 80 bool channelSequence = hasChannelSequence.Value; 81 bool powerRegister = hasPowerRegister.Value; 82 ChannelCount = channelCount.Value; 83 WatchdogCount = watchdogCount.Value; 84 this.hasChannelSelect = hasChannelSelect.Value; 85 86 if(WatchdogCount < 1 || WatchdogCount > 3) 87 { 88 throw new ConstructionException("Invalid watchdog count"); 89 } 90 91 analogWatchdogFlags = new IFlagRegisterField[WatchdogCount]; 92 analogWatchdogHighValues = new IValueRegisterField[WatchdogCount]; 93 analogWatchdogLowValues = new IValueRegisterField[WatchdogCount]; 94 if(WatchdogCount >= 2) 95 { 96 analogWatchdog2SelectedChannels = new IFlagRegisterField[ChannelCount]; 97 } 98 if(WatchdogCount == 3) 99 { 100 analogWatchdog3SelectedChannels = new IFlagRegisterField[ChannelCount]; 101 } 102 103 registers = new DoubleWordRegisterCollection(this, BuildRegistersMap(calibration, prescaler, vbatPin, channelSequence, powerRegister)); 104 105 IRQ = new GPIO(); 106 this.dmaChannel = dmaChannel; 107 this.dma = dmaPeripheral; 108 this.referenceVoltage = referenceVoltage; 109 this.externalEventFrequency = externalEventFrequency; 110 111 samplingThread = machine.ObtainManagedThread(StartSampling, externalEventFrequency); 112 channelSelected = new bool[ChannelCount]; 113 sampleProvider = new SensorSamplesFifo<ScalarSample>[ChannelCount]; 114 for(var channel = 0; channel < ChannelCount; channel++) 115 { 116 sampleProvider[channel] = new SensorSamplesFifo<ScalarSample>(); 117 } 118 Reset(); 119 } 120 FeedVoltageSampleToChannel(int channel, string path)121 public void FeedVoltageSampleToChannel(int channel, string path) 122 { 123 ValidateChannel(channel); 124 sampleProvider[channel].FeedSamplesFromFile(path); 125 } 126 FeedVoltageSampleToChannel(int channel, decimal valueInmV, uint repeat)127 public void FeedVoltageSampleToChannel(int channel, decimal valueInmV, uint repeat) 128 { 129 ValidateChannel(channel); 130 var sample = new ScalarSample(valueInmV); 131 for(var i = 0; i < repeat; i++) 132 { 133 sampleProvider[channel].FeedSample(sample); 134 } 135 } 136 SetDefaultValue(decimal valueInmV, int? channel = null)137 public void SetDefaultValue(decimal valueInmV, int? channel = null) 138 { 139 if(channel != null) 140 { 141 ValidateChannel(channel.Value); 142 sampleProvider[channel.Value].DefaultSample.Value = valueInmV; 143 return; 144 } 145 for(var i = 0; i < ChannelCount; i++) 146 { 147 sampleProvider[i].DefaultSample.Value = valueInmV; 148 } 149 } 150 Reset()151 public void Reset() 152 { 153 RegistersCollection.Reset(); 154 for(var i = 0; i < ChannelCount; i++) 155 { 156 channelSelected[i] = false; 157 } 158 currentChannel = 0; 159 awaitingConversion = false; 160 enabled = false; 161 externalTrigger = false; 162 sequenceInProgress = false; 163 sequenceCounter = 0; 164 samplingThread.Stop(); 165 } 166 ReadDoubleWord(long offset)167 public uint ReadDoubleWord(long offset) 168 { 169 return RegistersCollection.Read(offset); 170 } 171 WriteDoubleWord(long offset, uint value)172 public void WriteDoubleWord(long offset, uint value) 173 { 174 RegistersCollection.Write(offset, value); 175 } 176 177 public DoubleWordRegisterCollection RegistersCollection { get => registers; } 178 public long Size => 0x400; 179 public GPIO IRQ { get; } 180 ValidateChannel(int channel)181 private void ValidateChannel(int channel) 182 { 183 if(channel >= ChannelCount || channel < 0) 184 { 185 throw new RecoverableException($"Invalid argument value: {channel}. This peripheral implements only channels in range 0-{ChannelCount-1}"); 186 } 187 } 188 UpdateInterrupts()189 private void UpdateInterrupts() 190 { 191 var adcReady = adcReadyFlag.Value && adcReadyInterruptEnable.Value; 192 var analogWatchdog = analogWatchdogsInterruptEnable.Zip(analogWatchdogFlags, (enable, flag) => 193 { 194 return enable.Value && flag.Value; 195 }).Any(flag => flag); 196 var endOfSampling = endOfSamplingFlag.Value && endOfSamplingInterruptEnable.Value; 197 var endOfConversion = endOfConversionFlag.Value && endOfConversionInterruptEnable.Value; 198 var endOfSequence = endOfSequenceFlag.Value && endOfSequenceInterruptEnable.Value; 199 200 IRQ.Set(adcReady || analogWatchdog || endOfSampling || endOfConversion || endOfSequence); 201 } 202 StartSampling()203 private void StartSampling() 204 { 205 if(sequenceInProgress) 206 { 207 if(waitFlag.Value) 208 { 209 awaitingConversion = true; 210 return; 211 } 212 this.Log(LogLevel.Warning, "Issued a start event before the last sequence finished"); 213 return; 214 } 215 if(hasChannelSelect) 216 { 217 currentChannel = (scanDirection.Value == ScanDirection.Ascending) ? 0 : ChannelCount - 1; 218 } 219 else 220 { 221 sequenceCounter = (scanDirection.Value == ScanDirection.Ascending) ? 0 : (int)regularSequenceLength.Value; 222 currentChannel = (int)regularSequence[sequenceCounter].Value; 223 } 224 sequenceInProgress = true; 225 startFlag.Value = true; 226 SampleNextChannel(); 227 } 228 SendDmaRequest()229 private void SendDmaRequest() 230 { 231 if(dma != null) 232 { 233 dma.RequestTransfer(dmaChannel); 234 } 235 else 236 { 237 this.Log(LogLevel.Warning, "Received DMA transfer request, but no DMA is configured for this peripheral."); 238 } 239 } 240 WatchdogEnabled(int watchdogNumber)241 private bool WatchdogEnabled(int watchdogNumber) 242 { 243 switch(watchdogNumber) 244 { 245 case 0: 246 var enabledOnAll = !analogWatchdogSingleChannel.Value; 247 var enabledOnCurrent = enabledOnAll || (int)analogWatchdogChannel.Value == currentChannel; 248 return analogWatchdogEnable.Value && enabledOnCurrent; 249 case 1: 250 return analogWatchdog2SelectedChannels[currentChannel].Value; 251 case 2: 252 return analogWatchdog3SelectedChannels[currentChannel].Value; 253 } 254 throw new Exception("Unreachable, the watchdog count is checked in the constructor"); 255 } 256 SampleNextChannel()257 private void SampleNextChannel() 258 { 259 // Exit when peripheral is not enabled 260 if(!enabled) 261 { 262 currentChannel = 0; 263 sequenceCounter = 0; 264 sequenceInProgress = false; 265 return; 266 } 267 268 Func<bool> iterationFinished = null; 269 if(hasChannelSelect) 270 { 271 iterationFinished = () => currentChannel >= ChannelCount; 272 } 273 else 274 { 275 iterationFinished = () => sequenceCounter > (int)regularSequenceLength.Value; 276 } 277 278 while(!iterationFinished() && currentChannel >= 0) 279 { 280 if(hasChannelSelect && !channelSelected[currentChannel]) 281 { 282 SwitchToNextChannel(); 283 continue; 284 } 285 else 286 { 287 data.Value = GetSampleFromChannel(currentChannel); 288 if(dmaEnabled.Value) 289 { 290 SendDmaRequest(); 291 } 292 endOfSamplingFlag.Value = true; 293 294 for(int i = 0; i < WatchdogCount; i++) 295 { 296 if(WatchdogEnabled(i)) 297 { 298 if(data.Value > analogWatchdogHighValues[i].Value || data.Value < analogWatchdogLowValues[i].Value) 299 { 300 analogWatchdogFlags[i].Value = true; 301 this.Log(LogLevel.Debug, "Analog watchdog {0} flag raised for value {1} on channel {2}", i, data.Value, currentChannel); 302 } 303 } 304 } 305 endOfConversionFlag.Value = true; 306 UpdateInterrupts(); 307 this.Log(LogLevel.Debug, "Sampled channel {0}", currentChannel); 308 SwitchToNextChannel(); 309 return; 310 } 311 } 312 this.Log(LogLevel.Debug, "No more channels enabled"); 313 endOfSequenceFlag.Value = true; 314 sequenceInProgress = false; 315 sequenceCounter = 0; 316 UpdateInterrupts(); 317 startFlag.Value = false; 318 319 if(awaitingConversion) 320 { 321 awaitingConversion = false; 322 StartSampling(); 323 } 324 } 325 SwitchToNextChannel()326 private void SwitchToNextChannel() 327 { 328 if(hasChannelSelect) 329 { 330 currentChannel = (scanDirection.Value == ScanDirection.Ascending) ? currentChannel + 1 : currentChannel - 1; 331 } 332 else 333 { 334 sequenceCounter = (scanDirection.Value == ScanDirection.Ascending) ? sequenceCounter + 1 : sequenceCounter - 1; 335 // NOTE: Sequence finishes when `sequenceCounter` is either greater than `regularSequenceLength` or less than `0`. 336 // In both of those cases, we assume that at this point `currentChannel` will contain invalid value. 337 if(sequenceCounter >= 0 && sequenceCounter <= (int)regularSequenceLength.Value) 338 { 339 currentChannel = (int)regularSequence[sequenceCounter].Value; 340 } 341 } 342 } 343 GetSampleFromChannel(int channelNumber)344 private uint GetSampleFromChannel(int channelNumber) 345 { 346 var sample = sampleProvider[channelNumber].Sample; 347 sampleProvider[channelNumber].TryDequeueNewSample(); 348 return MilivoltsToSample((double)sample.Value); 349 } 350 MilivoltsToSample(double sampleInMilivolts)351 private uint MilivoltsToSample(double sampleInMilivolts) 352 { 353 ushort resolutionInBits; 354 switch(resolution.Value) 355 { 356 case Resolution.Bits6: 357 resolutionInBits = 6; 358 break; 359 case Resolution.Bits8: 360 resolutionInBits = 8; 361 break; 362 case Resolution.Bits10: 363 resolutionInBits = 10; 364 break; 365 case Resolution.Bits12: 366 resolutionInBits = 12; 367 break; 368 default: 369 throw new Exception("This should never have happend!"); 370 } 371 372 uint referencedValue = (uint)Math.Round((sampleInMilivolts / (referenceVoltage * 1000)) * ((1 << resolutionInBits) - 1)); 373 if(align.Value == Align.Left) 374 { 375 referencedValue = referencedValue << (16 - resolutionInBits); 376 } 377 return referencedValue; 378 } 379 BuildRegistersMap(bool hasCalibration, bool hasPrescaler, bool hasVbatPin, bool hasChannelSequence, bool hasPowerRegister)380 private Dictionary<long, DoubleWordRegister> BuildRegistersMap(bool hasCalibration, bool hasPrescaler, bool hasVbatPin, bool hasChannelSequence, bool hasPowerRegister) 381 { 382 var isrRegister = new DoubleWordRegister(this) 383 .WithFlag(0, out adcReadyFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "ADRDY") 384 .WithFlag(1, out endOfSamplingFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "EOSMP") 385 .WithFlag(2, out endOfConversionFlag, FieldMode.Read | FieldMode.WriteOneToClear, writeCallback: (_, val) => 386 { 387 if(val) 388 { 389 // Clearing the End Of Conversion flag triggers next conversion 390 // This function call must be delayed to avoid deadlock on registers access 391 machine.LocalTimeSource.ExecuteInNearestSyncedState((___) => SampleNextChannel()); 392 } 393 }, name: "EOC") 394 .WithFlag(3, out endOfSequenceFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "EOSEQ") 395 .WithFlag(4, out adcOverrunFlag, FieldMode.Read | FieldMode.WriteOneToClear, name: "OVR") 396 .WithReservedBits(5, 2) 397 .WithFlags(7, WatchdogCount, out analogWatchdogFlags, FieldMode.Read | FieldMode.WriteOneToClear, name: "AWD") 398 .WithReservedBits(7 + WatchdogCount, 4 - WatchdogCount) 399 .WithReservedBits(13, 19); 400 401 var interruptEnableRegister = new DoubleWordRegister(this) 402 .WithFlag(0, out adcReadyInterruptEnable, name: "ADRDYIE") 403 .WithFlag(1, out endOfSamplingInterruptEnable, name: "EOSMPIE") 404 .WithFlag(2, out endOfConversionInterruptEnable, name: "EOCIE") 405 .WithFlag(3, out endOfSequenceInterruptEnable, name: "EOSEQIE") 406 .WithFlag(4, out adcOverrunInterruptEnable, name: "OVRIE") 407 .WithReservedBits(5, 2) 408 .WithFlags(7, WatchdogCount, out analogWatchdogsInterruptEnable, name: "AWDIE") 409 .WithReservedBits(7 + WatchdogCount, 4 - WatchdogCount) 410 .WithReservedBits(13, 19) 411 .WithWriteCallback((_, __) => UpdateInterrupts()); 412 413 if(hasCalibration) 414 { 415 isrRegister 416 .WithTaggedFlag("EOCAL", 11) 417 .WithTaggedFlag("LDORDY", 12); 418 interruptEnableRegister 419 .WithTaggedFlag("EOCALIE", 11) 420 .WithTaggedFlag("LDORDYIE", 12); 421 } 422 else 423 { 424 isrRegister 425 .WithReservedBits(11, 2); 426 interruptEnableRegister 427 .WithReservedBits(11, 2); 428 } 429 430 var configurationRegister1 = new DoubleWordRegister(this) 431 .WithFlag(0, out dmaEnabled, name: "DMAEN") 432 .WithTaggedFlag("DMACFG", 1) 433 // When fully configurable channel sequencer is available, the SCANDIR and RES fields are swapped 434 .WithEnumField<DoubleWordRegister, ScanDirection>(hasChannelSequence ? 4 : 2, 1, out scanDirection, name: "SCANDIR") 435 .WithEnumField<DoubleWordRegister, Resolution>(hasChannelSequence ? 2 : 3, 2, out resolution, name: "RES") 436 .WithEnumField<DoubleWordRegister, Align>(5, 1, out align, name: "ALIGN") 437 .WithTag("EXTSEL", 6, 2) 438 .WithReservedBits(9, 1) 439 .WithValueField(10, 2, writeCallback: (_, val) => 440 { 441 // On hardware it is possible to configure on which edge should the trigger fire 442 // This Peripheral mocks external trigger using `externalEventFrequency`, so we only distinguish between manual/external trigger 443 externalTrigger = (val > 0); 444 }, name: "EXTEN") 445 .WithTaggedFlag("OVRMOD", 12) 446 .WithTaggedFlag("CONT", 13) 447 .WithFlag(14, out waitFlag, name: "WAIT") 448 .WithTaggedFlag("DISCEN", 16) 449 .WithReservedBits(17, 4) 450 .WithFlag(22, out analogWatchdogSingleChannel, name: "AWDSGL") 451 .WithFlag(23, out analogWatchdogEnable, name: "AWDEN") 452 .WithReservedBits(24, 2) 453 .WithValueField(26, 5, out analogWatchdogChannel, name: "AWDCH") 454 .WithReservedBits(31, 1); 455 456 if(!hasPowerRegister) 457 { 458 configurationRegister1 459 .WithTaggedFlag("AUTOFF", 15); 460 } 461 else 462 { 463 configurationRegister1 464 .WithReservedBits(15, 1); 465 } 466 467 var regularSequence1 = new DoubleWordRegister(this); 468 469 if(hasChannelSequence) 470 { 471 if(hasChannelSelect) 472 { 473 configurationRegister1 474 .WithFlag(21, name: "CHSELRMOD"); // no actual logic, but software expects to read the value back 475 } 476 else 477 { 478 regularSequence1 479 .WithValueField(0, 4, out regularSequenceLength) 480 .WithReservedBits(28, 4); 481 482 for(var i = 0; i < 4; ++i) 483 { 484 var j = i; 485 regularSequence1 486 .WithReservedBits(4 + 6 * i, 2) 487 .WithValueField(6 + 6 * i, 4, out regularSequence[j]); 488 } 489 } 490 } 491 else 492 { 493 configurationRegister1 494 .WithReservedBits(21, 1); 495 496 regularSequence1 497 .WithReservedBits(0, 32); 498 } 499 500 var configurationRegister2 = new DoubleWordRegister(this) 501 .WithReservedBits(0, 30) 502 .WithTag("CKMODE", 30, 2); 503 504 var commonConfigurationRegister = new DoubleWordRegister(this) 505 .WithReservedBits(0, 18) 506 .WithTaggedFlag("VREFEN", 22) 507 .WithTaggedFlag("TSEN", 23) 508 .WithReservedBits(25, 7); 509 510 if(hasPrescaler) 511 { 512 commonConfigurationRegister 513 .WithValueField(18, 4, name: "PRESC"); 514 } 515 else 516 { 517 commonConfigurationRegister 518 .WithReservedBits(18, 4); 519 } 520 521 if(hasVbatPin) 522 { 523 commonConfigurationRegister 524 .WithTaggedFlag("VBATEN", 24); 525 } 526 else 527 { 528 commonConfigurationRegister 529 .WithReservedBits(24, 1); 530 } 531 532 var registers = new Dictionary<long, DoubleWordRegister> 533 { 534 {(long)Registers.InterruptAndStatus, isrRegister}, 535 {(long)Registers.InterruptEnable, interruptEnableRegister}, 536 {(long)Registers.Control, new DoubleWordRegister(this) 537 .WithFlag(0, valueProviderCallback: _ => enabled, writeCallback: (_, val) => 538 { 539 if(val) 540 { 541 enabled = true; 542 adcReadyFlag.Value = true; 543 UpdateInterrupts(); 544 } 545 }, name: "ADEN") 546 // Reading one from below field would mean that command is in progress. This is never the case in this model 547 .WithFlag(1, valueProviderCallback: _ => false, writeCallback: (_, val) => { if(val) enabled = false; }, name: "ADDIS") 548 // Reading one from this field means that conversion is in progress 549 .WithFlag(2, out startFlag, writeCallback: (_, val) => 550 { 551 if(val) 552 { 553 if(externalTrigger) 554 { 555 samplingThread.Start(); 556 } 557 else 558 { 559 StartSampling(); 560 } 561 } 562 },name: "ADSTART") 563 .WithReservedBits(3, 1) 564 // Reading one from below field would mean that command is in progress. This is never the case in this model 565 .WithFlag(4, valueProviderCallback: _ => false, writeCallback: (_, val) => 566 { 567 if(val) 568 { 569 samplingThread.Stop(); 570 sequenceInProgress = false; 571 } 572 }, name: "ADSTP") 573 .WithReservedBits(5, 23) 574 .WithFlag(28, name: "ADVREGEN") // no logic implemented, but software expects to read this flag back 575 .WithReservedBits(29, 2) 576 .WithTaggedFlag("ADCAL", 31) 577 }, 578 {(long)Registers.Configuration1, configurationRegister1}, 579 {(long)Registers.Configuration2, configurationRegister2}, 580 {(long)Registers.SamplingTime, new DoubleWordRegister(this) 581 .WithTag("SMP", 0, 3) 582 .WithReservedBits(3, 29) 583 }, 584 {(long)Registers.RegularSequence1, regularSequence1}, 585 {(long)Registers.DataRegister, new DoubleWordRegister(this) 586 .WithValueField(0, 16, out data, FieldMode.Read, readCallback: (_, __) => 587 { 588 endOfConversionFlag.Value = false; 589 // This function call must be delayed to avoid deadlock on registers access 590 if(sequenceInProgress) 591 { 592 machine.LocalTimeSource.ExecuteInNearestSyncedState((___) => SampleNextChannel()); 593 } 594 }, name: "DATA") 595 .WithReservedBits(16, 16) 596 }, 597 {(long)Registers.CommonConfiguration, commonConfigurationRegister}, 598 }; 599 600 // Optional registers 601 if(hasChannelSelect) 602 { 603 registers.Add((long)Registers.ChannelSelection, new DoubleWordRegister(this) 604 .WithFlags(0, ChannelCount, 605 valueProviderCallback: (id, __) => channelSelected[id], 606 writeCallback: (id, _, val) => { this.Log(LogLevel.Debug, "Channel {0} enable set as {1}", id, val); channelSelected[id] = val; }) 607 .WithReservedBits(ChannelCount, 32 - ChannelCount) 608 ); 609 } 610 611 if(WatchdogCount >= 1) 612 { 613 registers.Add((long)Registers.Watchdog1Threshold, new DoubleWordRegister(this) 614 .WithValueField(0, 12, out analogWatchdogLowValues[0], name: "LT") 615 .WithReservedBits(12, 4) 616 .WithValueField(16, 12, out analogWatchdogHighValues[0], name: "HT") 617 .WithReservedBits(28, 4)); 618 } 619 if(WatchdogCount >= 2) 620 { 621 registers.Add((long)Registers.Watchdog2Threshold, new DoubleWordRegister(this) 622 .WithValueField(0, 12, out analogWatchdogLowValues[1], name: "LT") 623 .WithReservedBits(12, 4) 624 .WithValueField(16, 12, out analogWatchdogHighValues[1], name: "HT") 625 .WithReservedBits(28, 4)); 626 registers.Add((long)Registers.Watchdog2Configuration, new DoubleWordRegister(this) 627 .WithFlags(0, 14, out analogWatchdog2SelectedChannels, name: "AWD2CH") 628 .WithReservedBits(14, 18)); 629 } 630 if(WatchdogCount == 3) 631 { 632 // NOTE: If given implementation doesn't have channel selection, the third Watchdog Threshold will be under ChannelSelection offset 633 registers.Add(hasChannelSelect ? (long)Registers.Watchdog3Threshold : (long)Registers.ChannelSelection, new DoubleWordRegister(this) 634 .WithValueField(0, 12, out analogWatchdogLowValues[2], name: "LT") 635 .WithReservedBits(12, 4) 636 .WithValueField(16, 12, out analogWatchdogHighValues[2], name: "HT") 637 .WithReservedBits(28, 4)); 638 registers.Add((long)Registers.Watchdog3Configuration, new DoubleWordRegister(this) 639 .WithFlags(0, 14, out analogWatchdog3SelectedChannels, name: "AWD3CH") 640 .WithReservedBits(14, 18)); 641 } 642 643 if(hasCalibration) 644 { 645 registers.Add((long)Registers.CalibrationFactor, new DoubleWordRegister(this) 646 .WithValueField(0, 7, name: "CALFACT") 647 .WithReservedBits(7, 25)); 648 } 649 650 if(hasPowerRegister) 651 { 652 registers.Add((long)Registers.Power, new DoubleWordRegister(this) 653 .WithTaggedFlag("AUTOFF", 0) 654 .WithTaggedFlag("DPD", 1) // Deep-power-down mode 655 .WithReservedBits(2, 30)); 656 } 657 658 return registers; 659 } 660 661 private int currentChannel; 662 private int sequenceCounter; 663 private bool enabled; 664 private bool externalTrigger; 665 private bool sequenceInProgress; 666 private bool awaitingConversion; 667 private bool[] channelSelected; 668 669 private IEnumRegisterField<Align> align; 670 private IEnumRegisterField<Resolution> resolution; 671 private IEnumRegisterField<ScanDirection> scanDirection; 672 673 private IFlagRegisterField dmaEnabled; 674 private IFlagRegisterField analogWatchdogEnable; 675 private IFlagRegisterField startFlag; 676 private IFlagRegisterField waitFlag; 677 678 private IFlagRegisterField adcOverrunFlag; 679 private IFlagRegisterField adcReadyFlag; 680 private IFlagRegisterField[] analogWatchdogFlags; 681 private IFlagRegisterField endOfConversionFlag; 682 private IFlagRegisterField endOfSamplingFlag; 683 private IFlagRegisterField endOfSequenceFlag; 684 private IFlagRegisterField adcOverrunInterruptEnable; 685 private IFlagRegisterField adcReadyInterruptEnable; 686 private IFlagRegisterField[] analogWatchdogsInterruptEnable; 687 private IFlagRegisterField endOfConversionInterruptEnable; 688 private IFlagRegisterField endOfSamplingInterruptEnable; 689 private IFlagRegisterField endOfSequenceInterruptEnable; 690 private IFlagRegisterField analogWatchdogSingleChannel; 691 692 private IValueRegisterField data; 693 // Watchdog 1 either watches all channels or a single channel 694 private IValueRegisterField analogWatchdogChannel; 695 // While watchdogs 2 and 3 use bitfields for selecting channels to watch 696 private IFlagRegisterField[] analogWatchdog2SelectedChannels; 697 private IFlagRegisterField[] analogWatchdog3SelectedChannels; 698 private IValueRegisterField[] analogWatchdogHighValues; 699 private IValueRegisterField[] analogWatchdogLowValues; 700 701 private IValueRegisterField regularSequenceLength; 702 private IValueRegisterField[] regularSequence = new IValueRegisterField[MaximumSequenceLength]; 703 704 private readonly IDMA dma; 705 private readonly int dmaChannel; 706 private readonly bool hasChannelSelect; 707 private readonly uint externalEventFrequency; 708 private readonly double referenceVoltage; 709 private readonly IManagedThread samplingThread; 710 private readonly SensorSamplesFifo<ScalarSample>[] sampleProvider; 711 private readonly DoubleWordRegisterCollection registers; 712 private readonly IMachine machine; 713 714 private readonly int ChannelCount; 715 private readonly int WatchdogCount; 716 717 private const int MaximumSequenceLength = 16; 718 719 private enum Resolution 720 { 721 Bits12 = 0b00, 722 Bits10 = 0b01, 723 Bits8 = 0b10, 724 Bits6 = 0b11, 725 } 726 727 private enum ScanDirection 728 { 729 Ascending = 0b0, 730 Descending = 0b1, 731 } 732 733 private enum Align 734 { 735 Right = 0x0, 736 Left = 0x1, 737 } 738 739 private enum Registers 740 { 741 InterruptAndStatus = 0x00, // ADC_ISR 742 InterruptEnable = 0x04, // ADC_IER 743 Control = 0x08, // ADC_CR 744 Configuration1 = 0x0C, // ADC_CFGR1 745 Configuration2 = 0x10, // ADC_CFGR2 746 SamplingTime = 0x14, // ADC_SMPR 747 // Gap intended 748 Watchdog1Threshold = 0x20, // ADC_AWD1TR 749 Watchdog2Threshold = 0x24, // ADC_AWD2TR 750 ChannelSelection = 0x28, // ADC_CHSELR 751 Watchdog3Threshold = 0x2C, // ADC_AWD3TR 752 RegularSequence1 = 0x30, // ADC_SQR1 753 // Gap intended 754 DataRegister = 0x40, // ADC_DR 755 // Gap intended 756 Power = 0x44, // ADC_PWRR 757 // Gap intended 758 Watchdog2Configuration = 0xA0, // ADC_AWD2CR 759 Watchdog3Configuration = 0xA4, // ADC_AWD3CR 760 // Gap intended 761 CalibrationFactor = 0xC4, // ADC_CALFACT 762 // Gap intended 763 CommonConfiguration = 0x308, // ADC_CCR 764 } 765 } 766 } 767