0
\$\begingroup\$

I have 2 buttons connected to pulled-down ADC input. I want to trigger ISR(SIG_ADC) when ADC finishes conversion - this is working. If I start ADC using ADCSRA |= _BV(ADSC), this works OK;

I want ADC to start conversion automatically when there is HIGH edge on PB3. I suppose ADCSRA[ADATE] bit is responsible for this, but I can not make it work.

Buttons are connected this way (buttons can not be mechanically pressed simultaneously, so, R3 = 0 is OK) Chip is ATTiny45.

  # # # # # # # # # # # # # # # # # # # # # #
  #                                         #
  #                      20kΩ               #
  # GND >---------------- R1 ----*----> PB3 #
  #                              |          #
  #                              |          #
  #               (S1)   10KΩ    |          #
  #          *----.\_---- R2 ----*          #
  #          |                   |          #
  #          |                   |          #
  #          |    (S2)    0Ω     |          #
  #          *----.\_---- R3 ----*          #
  #          |                              #
  # Vcc >----*                              #
  #                                         #
  # # # # # # # # # # # # # # # # # # # # # #
  

I assume that ADATE should trigger conversion start when it's +2v on PB3, because ADCSRB |= _BV(ADTS1) | _BV(ADTS2); is set to Pin Change Interrupt Request value. And after conversion complete, ISR(SIG_ADC) will be triggered.

When I run ADCSRA |= _BV(ADSC) in main() loop, everything works as expected: PWM signal represents ADC value. When I comment this line, everything doesnt work, but I expect HIGH edge should trigger ADC and ADC should start conversion, and after some time it will fire ISR(SIG_ADC).

I can override this behavior attaching pin change interrupt to PB3 and running ADCSRA |= _BV(ADSC) from this interrupt, but I think that's wrong to attach both pin change interrupt and ADC to the same pin, I'd like ADC to auto-trigger on pin change.

Where is my mistake?

inline void setupPwm() {
  // Enable PLL
  _SFR_BYTE(PLLCSR) |= _BV(PLLE);
  // Wait for PLL to lock
  _delay_us(100);
  while (!(PLLCSR & _BV(PLOCK)));
  // Enable PCK clock on Timer1
  _SFR_BYTE(PLLCSR) |= _BV(PCKE);

  // Enable PWM
  _SFR_BYTE(TCCR1) =  _BV(COM1A0) | _BV(COM1A1) | _BV(PWM1A) | MOTOR_SPEED_SLOW;
  _SFR_BYTE(OCR1A) = 0x00;
  _SFR_BYTE(OCR1C) = 0xFF;
  _SFR_BYTE(TCNT1) = 0x00;
  _SFR_BYTE(DDRB)  |= _BV(PINB1);
}


inline void SetupAdc() {
  // Disable unnecessary data inputs
  // REF: 17.13.5 DIDR0 – Digital Input Disable Register 0
  // [5:2] = 1101 (ADC0D and ADC2D and ADC1D) => Disable ADC0, ADC1, ADC2.
  // NOTE: Bit order is irregular
  _SFR_BYTE(DIDR0) |= _BV(ADC0D) | _BV(ADC1D) | _BV(ADC2D);

  // Set ADC for PB3 (ADC3) button input. 
  // REF: 17.13.1 ADMUX – ADC Multiplexer Selection Register
  // [7:6],4 = 0 (REFS0..3) => VCC used as Voltage Reference, disconnected from PB0 (AREF).
  // [5] = 1 (ADLAR)        =>  Right\Left Adjust (1 = Left, means ADCH contains [10:2] of ADC value and ADCL contains [7:8] as [1:0] of ADC value)
  // [3:0] = 0011 (MUX0..3) =>  ADC3 (PB3)
  _SFR_BYTE(ADMUX) |= _BV(MUX0) | _BV(MUX1) | _BV(ADLAR);

  // Setup ADC
  // REF: 17.13.2 ADCSRA – ADC Control and Status Register A
  // [7] = 1 (ADEN)  => Enable ADC
  // [6] = 0 (ADSC)  => ADC Start Conversion
  // [5] = 1 (ADATE) => ADC Auto Trigger Enable
  // [4] = 0 (ADIF)  => ADC Interrupt Flag // Set by HW
  // [3] = 1 (ADIE)  => ADC Interrupt Enable
  // [2:0] = 0b111 (ADPS0..3) =>  ADC Prescaler Select Bits (0b111 => 1/128)  
  _SFR_BYTE(ADCSRA) |= _BV(ADEN) | _BV(ADATE) | _BV(ADIE) | _BV(ADPS0) | _BV(ADPS1) | _BV(ADPS2);


  //REF: 17.13.4 ADCSRB – ADC Control and Status Register B
  // [7] = 0 (BIN) => Bipolar Input Mode (0 = disabled)
  // [5] = 0 (IPR) => Input Polarity Reversal (0 = disabled)
  // [2:0] = 0b000 (ADTS0..2) => ADC Auto Trigger Source 
  _SFR_BYTE(ADCSRB) |= _BV(ADTS1) | _BV(ADTS2);

  // Enable external interrupts
  // REF: 9.3.2 GIMSK – General Interrupt Mask Register
  // [6] = 0 (INT0) => External Interrupt Request 0 Enable (0 = disable)
  // [5] = 1 (PCIE) => Pin Change Interrupt Enable (1 = enable)
  _SFR_BYTE(GIMSK) |= _BV(PCIE);

  // Globally enable interrupts
  // REF: 4.4.1 SREG – AVR Status Register
  // [7] = 1 (I) => Global Interrupt Enable
  _SFR_BYTE(SREG) |= _BV(SREG_I);
}


ISR(SIG_ADC) {
  _SFR_BYTE(OCR1A) = _SFR_BYTE(ADCH);
}

int main() {
  setupPwm();
  SetupAdc();
  _SFR_BYTE(OCR1A) = 0x7F;
  while(true){
     _SFR_BYTE(ADCSRA) |= _BV(ADSC); // THIS LINE
  };
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

You have selected the pin change interrupt as the ADC trigger source, but there is no code to configure pin change interrupts. So since nothing can trigger pin change interrupts, the ADC never triggers.

\$\endgroup\$
1
  • \$\begingroup\$ Tanks. Adding _SFR_BYTE(PCMSK) |= _BV(PCINT3); solved problem. \$\endgroup\$
    – filimonic
    Commented Mar 31, 2021 at 21:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.