Инициализация ADC А0 пина

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015
void setup() {

 int analog = analogRead(5);  // 1  работает

  ADMUX = 0  | _BV(ADLAR) | _BV(REFS0); // пин 0 - устанавливаем канал, используем 8 бит, ref=Vcc
  ADCSRA = _BV(ADEN) | // ADC enable
           _BV(ADSC) | // ADC start
           _BV(ADATE) | // Auto trigger
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); 

 // int analog = analogRead(5);   // 2   - если тут тот код стопорится 

  Serial.begin(9600);

}

void loop() {

  Serial.print("111111111111");

}

Здравствуйте. Помогите исправить настройку ADC порта А0, который перестает работать, если в коде есть еще операция чтения любого другого аналогового порта. Причем которая размещается именно ПОСЛЕ инициализации ADC.  

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

Это происходит потому что ADMUX меняется.

int analogRead(uint8_t pin)
{
	uint8_t low, high;

#if defined(analogPinToChannel)
#if defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#endif
	pin = analogPinToChannel(pin);
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
	if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
	if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
	if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
	if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(ADCSRB) && defined(MUX5)
	// the MUX5 bit of ADCSRB selects whether we're reading from channels
	// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
	ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
  
	// set the analog reference (high two bits of ADMUX) and select the
	// channel (low 4 bits).  this also sets ADLAR (left-adjust result)
	// to 0 (the default).
#if defined(ADMUX)
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
	ADMUX = (analog_reference << 4) | (pin & 0x07);
#else
	ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif
#endif

	// without a delay, we seem to read from the wrong channel
	//delay(1);

#if defined(ADCSRA) && defined(ADCL)
	// start the conversion
	sbi(ADCSRA, ADSC);

	// ADSC is cleared when the conversion finishes
	while (bit_is_set(ADCSRA, ADSC));

	// we have to read ADCL first; doing so locks both ADCL
	// and ADCH until ADCH is read.  reading ADCL second would
	// cause the results of each conversion to be discarded,
	// as ADCL and ADCH would be locked when it completed.
	low  = ADCL;
	high = ADCH;
#else
	// we dont have an ADC, return 0
	low  = 0;
	high = 0;
#endif

	// combine the two bytes
	return (high << 8) | low;
}

 

_Igor_
Offline
Зарегистрирован: 10.01.2022

1. После установки ADMUX надо паузу хотя бы 1ms

2. Надо дождаться ОКОНЧАНИЯ преобразования, считать данные и только после этого менять ADMUX 

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

ПОдскажите, пожалуйста, как его восстанавливать? У меня не высокий уровень знаний.

На А0 стоит потенциометр, опрашивается в прерывании по таймеру на 1 мс, который запускается в Setup.  Там же инициализируется ADC.  На А7 (с внешним резистором) и, когда потенциометр не крутится, нажимается кнопка (просто цифровых уже нет свободных). Кнопка опрашивается в loop. 

вот фрагмент кода обработки потенциометра

  ADMUX = pin  | _BV(ADLAR) | _BV(REFS0); // устанавливаем канал, используем 8 бит, ref=Vcc
  ADCSRA = _BV(ADEN) | // ADC enable
           _BV(ADSC) | // ADC start
           _BV(ADATE) | // Auto trigger
           //     _BV(ADIE) | // Interrupt enable
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz
  ADCSRB = 0;                // Free run mode, no high MUX bit
  DIDR0 = 1 << pin; // Turn off digital input for ADC pin

  sei();
  ADCSRA |= _BV(ADIE);    // Enable sampling interrupt // сразу запускаем АЦП
  
  delay(10);
  
  //Настраиваем таймер0 на прерывание по совпадению
  //Раз в 1 msec
  OCR0A = 0xAF;
  TIMSK0 |= _BV(OCIE0A);  // Enable processing interrupts   // сразу запускаем Таймер
  sei(); // Enable interrupts

  //  Serial.println((String)"  init end           ");
}

//Прерывания таймера 1 раз в  msec
ISR (TIMER0_COMPA_vect)
{
  uint8_t val;
  // Serial.println("  prer t0 est");
  if (abs(pedalCurrentPos - pedalAverADC) >= pedalStep)  //поехали
  {
    pedalCurrentPos = pedalAverADC;
    //  Serial.println((String)"      pedalAverADC " + pedalAverADC);
    val = map (pedalCurrentPos, pedalStart, pedalStop, 0, pedalSteps); // от 0 до pedalSteps (127)
    if (val != pedalVal)
    {
      pedalVal = val;

      if (fSP == 0)
      {
        PedalAction(pedalVal);
      }
      else
      {
        PedalActionP(pedalVal);
      }
    }
  }
}

ISR(ADC_vect) { // ADC sampling interrupt
  uint8_t  sample = ADCH; // 0-255
  //   Serial.println((String)"   bbbb      "); //

  uint8_t  noiseCnt = 0;

  if (sample > pMax) sample = pMax;
  if (sample < pMin) sample = pMin;
  noiseCnt = 0;
  pedalAverADC = GetNextAvrg(sample);
  //   Serial.println((String)"   AverADC            "+pedalAverADC);

 

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

еще меня устроит применение библы  "GyverButton.h",  но примеров ее работы с аналоговыми портами совместно с непосредственной обработкой А0, как выше   - не могу найти. Вот так только, что у меня не работает..

int analog = analogRead(7);  
bHold.tick(analog < 100);
if (bHold.isPress())

 

nik182
Offline
Зарегистрирован: 04.05.2015

Если уровень знаний низкий зачем использовать прямой доступ к регистрам? Аналогрид делает то же самое практически с той же скоростью и совершенно без проблем? И совместим с другими библиотеками?

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

_Igor_ пишет:

1. После установки ADMUX надо паузу хотя бы 1ms

2. Надо дождаться ОКОНЧАНИЯ преобразования, считать данные и только после этого менять ADMUX 

Верно ли что 1) таймер никак не связан с проблемой, а преобразование ADC начинается после изменения напряжения на входе А0?  2) Как объяснить "стоп" выполнения кода, когда нет никакого преобразования, даже если не подключен потенциометр? 3) смена / установка ADMUX видимо осуществляется в момент команды analogRead? Значит надо типа так: считали А7 (кнопку) и в конце ее обработки обратно сразу вернули ADMUX на А0? Так? 

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

nik182 пишет:
Если уровень знаний низкий зачем использовать прямой доступ к регистрам? Аналогрид делает то же самое практически с той же скоростью и совершенно без проблем? И совместим с другими библиотеками?

Проект по моему заказу пишется программистом, не мной. Небольшую доработку хочется сделать самому. Вот и все. Что такое Аналогрид я не знаю, и на остальные вопросы, извиняюсь, дать ответа не могу. Если нужны уточнения  - спрашивайте, и прошу помочь.

Верно, что что вопрос относится к мультиплексированию входных портов одного АЦП и нужно чтоб один порт работал по прерыванию, чтоб не менять уже отлаженного кода, а второй должен настроить я, чтоб опрашивался в цикле? 

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

dim3740 пишет:

Что такое Аналогрид я не знаю

Это строки 3 и 11 в коде первого поста

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

ясно. У меня Grid ассоциировался с сеткой и массивом.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Кабсдец.))) АналогРид (AnalogRead)! В коде так и написано. Вы чего? От 1 мая не отошли ещё?)))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Ну он то думал Анало-грид. 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

DetSimen пишет:

Ну он то думал Анало-грид. 

Ну как мне теперь это развидеть?!!!

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

Надеюсь на практическую помощь. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Так а какую именно помощь Вы надеетесь получить?

Физически АЦП один, следовательно, одновременно читать и из цикла и из прерывания невозможно. Надо как-то разносить по времени. А как - это уж Вам решать. Первое, что приходит в голову - устанавливать флаг и запрещать чтение либо оттуда, либо оттуда. Т.е. данные будут с пропусками. Надо расставлять приоритеты. Но скорее всего, никакой работы с регистрами для этого не понадобится. Т.е. все решается обычным analogRead(). Ну либо не решается никак.

b707
Offline
Зарегистрирован: 26.05.2017

dim3740 пишет:

Надеюсь на практическую помощь. 


на какую, простите, нахрен, помощь?
Вы заказали проект программисту и, наверно, платите ему - так что мешает попросить его добавить в код еще один порт...? С какой целью тут людям выносить мозг - чтобы сэкономить пару сотен рублей?

dim3740
dim3740 аватар
Offline
Зарегистрирован: 25.03.2015

andriano пишет:

Так а какую именно помощь Вы надеетесь получить?

Физически АЦП один, следовательно, одновременно читать и из цикла и из прерывания невозможно. Надо как-то разносить по времени. А как - это уж Вам решать. Первое, что приходит в голову - устанавливать флаг и запрещать чтение либо оттуда, либо оттуда. Т.е. данные будут с пропусками. Надо расставлять приоритеты. Но скорее всего, никакой работы с регистрами для этого не понадобится. Т.е. все решается обычным analogRead(). Ну либо не решается никак.

Я выше писал: "На А7 (с внешним резистором) и, когда потенциометр НЕ КРУТИТСЯ, нажимается кнопка (просто цифровых уже нет свободных)"

Это не означается разве, что есть "разноска по времени"?  Мне не хочется влезать в отдельно оформленную кастомную библиотеку по обработке потенциометра. Надо найти решение именно ДОБАВИТЬ опрос кнопки.  Вот типа такого можно?

void loop() {
  int analog = analogRead(7);
  if (analog < 200)
  {
    //// обработка кнопки на А7
    
    //затем восстанавливаем чтение А0
    ADMUX = pin  | _BV(ADLAR) | _BV(REFS0); // устанавливаем канал, используем 8 бит, ref=Vcc
    ADCSRA = _BV(ADEN) | // ADC enable
             _BV(ADSC) | // ADC start
             _BV(ADATE) | // Auto trigger
             //     _BV(ADIE) | // Interrupt enable
             _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
  }

}

 

Komandir
Komandir аватар
Offline
Зарегистрирован: 18.08.2018

После любого переключения входов на ADC - первое измерение покажет ХЗчто. Это у вас в коде нигде не отрабатывается.

Pyotr
Offline
Зарегистрирован: 12.03.2014

А почему не читать все аналоговые пины через прерывание ISR(ADC_vect) ? В прерывании сохранять значение АЦП и тут же запускать измерение на следующем канале. Даже если читать 8 каналов, то данные будут обновляться каждую мс. При этом в loop() читать  сохраненные значения из нужного канала.

nik182
Offline
Зарегистрирован: 04.05.2015

Сразу следующий не получится. Конденсатор в блоке АЦП один. Его надо перезарядить до уровня следующего канала на это надо время. Кто то задержки ставит, кто то два раза считывает и первое выбрасывает но в любом случае вподряд несколько разных каналов сразу без больших ошибок не получится. Единственная возможность сделать выходное сопротивление источника сигнала минимальным. Но это обычно из области фантастики без вложения денег.

Pyotr
Offline
Зарегистрирован: 12.03.2014

nik182 пишет:
Сразу следующий не получится. Конденсатор в блоке АЦП один. Его надо перезарядить до уровня следующего канала на это надо время. Кто то задержки ставит, кто то два раза считывает и первое выбрасывает но в любом случае вподряд несколько разных каналов сразу без больших ошибок не получится. Единственная возможность сделать выходное сопротивление источника сигнала минимальным. Но это обычно из области фантастики без вложения денег.

Источник с выходным сопротивлением <= 10кОм вполне справляется. А вот опорное если меняется, то задержка нужна.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

dim3740 пишет:

Я выше писал: "На А7 (с внешним резистором) и, когда потенциометр НЕ КРУТИТСЯ, нажимается кнопка (просто цифровых уже нет свободных)"

Это не означается разве, что есть "разноска по времени"?

МК не знает, КРУТИТСЯ потенциометр или нет. Поэтому никакого "разноса по времени" здесь не получится.

На всякий случай еще тут же приведу свой ответ на пост №6:

dim3740 пишет:

Верно ли что

1) таймер никак не связан с проблемой, а преобразование ADC начинается после изменения напряжения на входе А0? 

Неверно.

Цитата:

2) Как объяснить "стоп" выполнения кода, когда нет никакого преобразования, даже если не подключен потенциометр?

Никак. МК не знает, подключен ли потенциометр.

Соответственно, если преобразование задано в коде, наличие или отсутствие потенциометра, а также вращается он или нет, преобразование произойдет в любом случае.

 

В общем, меняйте логику программы так, чтобы чтение АЦП либо ВСЕГДА присходило только в цикле, либо ВСЕГДА только в прерывании.