Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug in ATtiny85 (ADC in free running mode) #136

Open
makkiato83 opened this issue Jan 8, 2023 · 4 comments
Open

Bug in ATtiny85 (ADC in free running mode) #136

makkiato83 opened this issue Jan 8, 2023 · 4 comments

Comments

@makkiato83
Copy link

I think there is a bug in the way the simulator handles ADC in free running mode.

This is a mode, documented in the datasheet, in which the ADC keeps sampling the chosen input.
In is configured using the ADCSRA and ADCSRB registers.

To reproduce the error I have prepared this sketch: https://wokwi.com/projects/353310932474416129

In the Wokwi simulation, there is only one message reaching the console.

In a physical Attiny85, the console keeps receiving messages, as it should.

BUG(?): It appears like the simulator somewhere does not properly re-start the ADC conversion.
This bug might (not checked) affect not just the free-running-mode (ADCSRB =0x00) but other interrupt-driven ADC-conversions as well (other values of ADCSRB).

@urish
Copy link
Contributor

urish commented Jan 8, 2023

From a quick glance at the ADC peripheral code, it seems like this functionality was never implemented. If you want to try to implement it and send a pull-request, that might work.

Otherwise, I can open it for voting, but I'm pretty sure it won't get much attention - the ADC has been out for more than a year, and no one every asked about the free running mode (until now, at least).

@makkiato83
Copy link
Author

Thanks for the quick reply!

No need to open it for voting, I am exploring the Attiny features purely for educational purposes and I don't need this feature.
I reported this bug just to let you know.

Keep up the great work!

@drf5n
Copy link

drf5n commented Feb 17, 2023

From a quick glance at the ADC peripheral code, it seems like this functionality was never implemented. If you want to try to implement it and send a pull-request, that might work.

Otherwise, I can open it for voting, but I'm pretty sure it won't get much attention - the ADC has been out for more than a year, and no one every asked about the free running mode (until now, at least).

I think to make it work, one would need to add a bit to this routine to schedule another completeADCRead dependent on
ADSCRB & bit(ADATE):

completeADCRead(value: number) {
const { ADCL, ADCH, ADMUX, ADCSRA } = this.config;
this.converting = false;
this.conversionCycles = 13;
if (this.cpu.data[ADMUX] & ADLAR) {
this.cpu.data[ADCL] = (value << 6) & 0xff;
this.cpu.data[ADCH] = value >> 2;
} else {
this.cpu.data[ADCL] = value & 0xff;
this.cpu.data[ADCH] = (value >> 8) & 0x3;
}
this.cpu.data[ADCSRA] &= ~ADSC;
this.cpu.setInterruptFlag(this.ADC);
}

But it gets complicated depending on the particular free running mode per ADSCRB & (0b111 << ADTS0)

The plain ADSCRB::ADTS[0:2]=0b000 free running mode trigger source might be a straightforward scheduling of a call to onADCRead(???), but the others trigger sources would seem to need something like interrupts.

 completeADCRead(value: number) { 
   const { ADCL, ADCH, ADMUX, ADCSRA } = this.config; 
   this.converting = false; 
   this.conversionCycles = 13; 
   if (this.cpu.data[ADMUX] & ADLAR) { 
     this.cpu.data[ADCL] = (value << 6) & 0xff; 
     this.cpu.data[ADCH] = value >> 2; 
   } else { 
     this.cpu.data[ADCL] = value & 0xff; 
     this.cpu.data[ADCH] = (value >> 8) & 0x3; 
   } 
   this.cpu.data[ADCSRA] &= ~ADSC; 
   this.cpu.setInterruptFlag(this.ADC); 
   if(this.cpu.data[ADSCRA & bit(ADATE)){
     switch(ADSCRB & (0b111 <<ADTS0)){
        case 0b000:
          this.cpu.addClockEvent(() => this.cpu.onADCRead(ADMUX???), this.sampleCycles)
          break;
          // 0b001-0b111 not handled
        default: ;
     }
   }
 } 

Datasheet ADSCRB reference: https://onlinedocs.microchip.com/pr/GUID-0EC909F9-8FB7-46B2-BF4B-05290662B5C3-en-US-12.1.1/index.html

@ArminJo
Copy link

ArminJo commented May 30, 2024

Here is another bug of ADC freerunning mode in 328P https://wokwi.com/projects/393410516428554241.
It works, but it swaps channnels, even during the loop. Only the first loop seems to be correct.

uint16_t sArray1[16];
uint16_t sArray2[16];
int sLoopCounter;
void setup() {
  Serial.begin(115200);
}
void loop() {
  Serial.print(F("sLoopCounter="));
  Serial.println(sLoopCounter++);

  // MVCE for https://github.com/wokwi/avr8js/issues/136
  ADMUX = 0xC0; // select A0
  ADCSRA = ((1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIF) | 5);
  for (unsigned int i = 0; i < 16; i++) {
    loop_until_bit_is_set(ADCSRA, ADIF);
    ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished with "loop_until_bit_is_set(ADCSRA, ADIF)".
    uint16_t tValue = ADCL | (ADCH << 8);
    sArray1[i] = tValue; // store value at current counter index
  }
  ADCSRA = ((1 << ADEN) | (1 << ADIF) | 5); // Disable auto-triggering (free running mode), but the last conversion is still running
  delay(1);
  ADMUX = 0xC1;// select A1
  ADCSRA = ((1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIF) | 5);
  for (unsigned int i = 0; i < 16; i++) {
    loop_until_bit_is_set(ADCSRA, ADIF);
    ADCSRA |= _BV(ADIF); // clear bit to enable recognizing next conversion has finished with "loop_until_bit_is_set(ADCSRA, ADIF)".
    uint16_t tValue = ADCL | (ADCH << 8);
    sArray2[i] = tValue; // store value at current counter index
  }
  ADCSRA = ((1 << ADEN) | (1 << ADIF) | 5); // Disable auto-triggering (free running mode), but the last conversion is still running

  Serial.println(F("Print i=<ValueA0>|<ValueA1>"));
  /*
     We expect i=<ValueA0>|<ValueA1>, but this is only true the first time after boot,
     Next loops we see i=<ValueA1>|<ValueA0>
     and after 12 to 13 samples i=<ValueA0>|<ValueA0> or i=<ValueA1>|<ValueA1>
  */
  for (unsigned int i = 0; i < 16; i++) {
    Serial.print(i);
    Serial.print('=');
    Serial.print(sArray1[i]);
    Serial.print('|');
    Serial.print(sArray2[i]);
    Serial.print(F(", "));
  }
  Serial.println();

  delay(2000);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants