Помощь в разборе кода

Lictor
Offline
Зарегистрирован: 01.10.2015

Всем доброго дня. 

Скачал код, который позволяет измерить напряжение питания ардуино на основе внутреннего источника опорного напряжения. Но возникла куча вопросов:

1. Что бы использовать источник 1.1В нужно Биты 7-6 REFS1 и REFS0 в Регистре ADMUX установить в 1, но в коде ставится только REFS0

2. Вывод 7 есть на atmega328 в smd исполнении, но нет в DIP. Где тогда происходит измерение?

3. По завершению кода не надо ли переключиться на входное напряжение в качестве опорного?

 

Спасибо.

void setup() {
  Serial.begin(9600);
  Serial.println("start");
  Serial.println(readVcc());
}

void loop() {

}

long readVcc() {
  //https://sites.google.com/site/100voltsamper/mikrokontroller-razbor-poleetov/acp-v-atmega328p-pod-kakim-sousom-ego-neobhodimo-podavat
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference

  // Зададим уставки препроцессора:
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)  // Если "тип котроллера" или "тип контроллера"..., то
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) // Сокращ. if else
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else  // т.к. контроллера "__AVR_ATmega328P__" нет в условиях, то выполняем следующую строку:
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);  
    // (| - Оператор побитового включающего или, ADMUX - Регистр настройки мультиплексора АЦП)
    // _BV()поразрядный сдвиг
    // _BV(REFS0) пишем 1 в REFS0, получаем REFS1=0 (по умолчанию), REFS0=1, т.о. Подключено напряжение питания AVcc
    //Биты 3-0 MUX3, MUX2, MUX1 и MUX0. Данная группа битов определяет какой вход будет активен в качестве входа для АЦП.
    //номера пина входного канала 0111  ADC7 , этот пин вроде стандартно в ардуино не используется, хотя есть на SMD atmega328
    //т.о. получаем, что контроллер настроен на считывание внешнего питания на 7 пине. (???)
      #endif  

  delay(75); // Wait for Vref to settle  // ждем стабилизации опорного напряжения (возможно при вызове кода после секунды работы контроллера задержка не требуется?)
  ADCSRA |= _BV(ADSC); // Start conversion  //ADCSRA - Регистр управления и статуса А,|= запишем 1, данный бит включает режим преобразования входного аналогового сигнал в двоичный код, затем опять становится 0
  while (bit_is_set(ADCSRA,ADSC)); // measuring  //Проёверка на включенность (1) бита, когда включается 0, измерение закончено, выходим из цикла
 
  
  /*Полученный результат преобразования записывается в 2 регистра ADCH и ADCL.
  В зависимости от направления выравнивания преобразования старшийе и младшие разряды заносятся в разные регистры*/
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;  // объединяем значения тсаршего и младшего бита, что бы получить значение.

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000  // вычисляем результат
  return result; // Vcc in millivolts  возвращаем результат
}

 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Если взглянуть в приведенный вами код, то можно там увидеть - 

13   // Read 1.1V reference against AVcc
14   // set the reference to Vcc and the measurement to the internal 1.1V reference

что означает, что измерятся напряжение на внутреннем источнике опорного напряжения, когда для АЦП опорным явялется AVcc

И тогда все становится ясным

1.  "ставится только REFS0" -> Источник опорного напряжения AVcc
2.  "Вывод 7" это_BV(MUX2) | _BV(MUX1) | _BV(MUX0)  а 
_BV(MUX3) | _BV(MUX2) | _BV(MUX1) это подключение на измерение внутреннего источнока 1.1В. Измерение происходит внутри

3.  Не надо, так как оно уже итак есть

Lictor
Offline
Зарегистрирован: 01.10.2015

Спасибо!

Lictor
Offline
Зарегистрирован: 01.10.2015

Еще хотел уточнить, строка 33 нужна только при первом запуске функции, потом (или если до вызова функции) прошло достаточно времени, то задержка не нужна?

Если я правильно понимаю, то здесь мы ждем стабилизации напряжения на внутреннем источнике 1,1В, которое после того как установится (после запуска контроллера) не меняется, не отключается и вообще не меняется за весь период работы. 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Lictor пишет:

Если я правильно понимаю, то здесь мы ждем стабилизации напряжения на внутреннем источнике 1,1В, которое после того как установится (после запуска контроллера) не меняется, не отключается и вообще не меняется за весь период работы. 

Не совсем. Источник опорного напряжения 1.1В  достаточно высокоомный. Поэтому после переключения на него канала АЦП нужно подождать пока зарядится входной измерительный конденсатор. Если потом каналы АЦП не переключаются, то при последующиъ измерениях можно задержку не делать. И по любому 75 миллисекунд это перебор. Одной-двух вполне достаточно.

Lictor
Offline
Зарегистрирован: 01.10.2015

asam пишет:

Lictor пишет:

Если я правильно понимаю, то здесь мы ждем стабилизации напряжения на внутреннем источнике 1,1В, которое после того как установится (после запуска контроллера) не меняется, не отключается и вообще не меняется за весь период работы. 

Не совсем. Источник опорного напряжения 1.1В  достаточно высокоомный. Поэтому после переключения на него канала АЦП нужно подождать пока зарядится входной измерительный конденсатор. Если потом каналы АЦП не переключаются, то при последующиъ измерениях можно задержку не делать. И по любому 75 миллисекунд это перебор. Одной-двух вполне достаточно.

Благодарю, уменьшу задержку до 3 миллисекунд. 

P.S. пробовал убрать задержку, при первом запуске показывает заниженное напряжение питания, потом нормально, при задержке хотя бы в  1 миллисекунду показывает корректно.

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

Lictor пишет:

 пробовал убрать задержку, при первом запуске показывает заниженное напряжение питания, потом нормально, при задержке хотя бы в  1 миллисекунду показывает корректно.

можно вовсе выкинуть задержку и просто повторить строчки 34 и 35 дважды

rkit
Offline
Зарегистрирован: 23.11.2016

Выкинуть задержку, и поставить мусорный код, который задерживает выполнение. Можно, конечно. Только зачем.

Lictor
Offline
Зарегистрирован: 01.10.2015

На данный момент задержку реализовал так:

  unsigned long previousMillis = millis();  //задержка для стабилизации напряжения
  while (millis() - previousMillis < 5);

 

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

Lictor пишет:

На данный момент задержку реализовал так:

  unsigned long previousMillis = millis();  //задержка для стабилизации напряжения
  while (millis() - previousMillis < 5);

 

чем это отличается от delay(5)?

Lictor
Offline
Зарегистрирован: 01.10.2015

Ну в принципе особо ничем. Но delay() мне не нравится на уровне веры, а способа лучше дождаться выравнивания напряжения я не знаю. 

void setup() {
  Serial.begin(115200);
  Serial.println("start");
  pinMode(A0, INPUT);
}

void loop() {
  Serial.print(readVcc());
  Serial.print(" A0=");
  Serial.println(analogRead(A0));
}

long readVcc() {
  // Read 1.1V reference against AVcc, set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)  // Если "тип котроллера" или "тип контроллера"..., то
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) // Сокращ. if else
  ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);
#else  // т.к. контроллера "__AVR_ATmega328P__" нет в условиях, то выполняем следующую строку:
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  // (| - Оператор побитового включающего или, ADMUX - Регистр настройки мультиплексора АЦП) _BV()поразрядный сдвиг
  // _BV(REFS0) пишем 1 в REFS0, получаем REFS1=0 (по умолчанию), REFS0=1, т.о. Подключено напряжение питания AVcc
  //Биты 3-0 MUX3, MUX2, MUX1 и MUX0. Данная группа битов определяет какой вход будет активен в качестве входа для АЦП.
#endif

  unsigned long previousMillis = millis();  //задержка для стабилизации напряжения
  while (millis() - previousMillis < 5);

  ADCSRA |= _BV(ADSC); //ADCSRA - Регистр управления и статуса А,|= запишем 1, данный бит включает режим преобразования входного аналогового сигнал в двоичный код, затем опять становится 0
  while (bit_is_set(ADCSRA, ADSC)); // measuring  //Проёверка на включенность (1) бита, когда включается 0, измерение закончено, выходим из цикла
  uint8_t low  = ADCL; // Полученный результат преобразования записывается в 2 регистра ADCH и ADCL.
  uint8_t high = ADCH;
  long result = (high << 8) | low; // объединяем значения тсаршего и младшего бита, что бы получить значение.
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000  // вычисляем результат
  return result; // Vcc in millivolts  возвращаем результат
}

 

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

Понимаешь ли.  В стандартном delay(), унутре, есть вызов функции yield(), что дает знающим людям хотя бы видимость неблокирующей задершки.  В твоём while этого совсем нихрена нет. 

Не так страшен delay() как неверующие в него, отрицающие и изгоняющие его. :)  

Lictor
Offline
Зарегистрирован: 01.10.2015

DetSimen пишет:

Понимаешь ли.  В стандартном delay(), унутре, есть вызов функции yield(), что дает знающим людям хотя бы видимость неблокирующей задершки.  В твоём while этого совсем нихрена нет. 

Не так страшен delay() как неверующие в него, отрицающие и изгоняющие его. :)  

 

Не знал такого, никогда не встречал описания реализации delay(), везде написано только, что delay() зло и используйте millis()

 

А если так?

  unsigned long previousMillis = millis();  //задержка для стабилизации напряжения
  while (millis() - previousMillis < 5) yield();

 

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

DetSimen пишет:

Lictor пишет:

На данный момент задержку реализовал так:

  unsigned long previousMillis = millis();  //задержка для стабилизации напряжения
  while (millis() - previousMillis < 5);

 

чем это отличается от delay(5)?

Отличается принципиально: delay(5) обеспечивает точность интервала где-то 0.05% в используемых единицах, а вышеприведенный код - хуже 20%.

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

да ему точность задершки, я думаю, не так уж и важна. Наерна. 

Какая разность скока налили, 400 капель, или 402. 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

DetSimen пишет:

Понимаешь ли.  В стандартном delay(), унутре, есть вызов функции yield(), что дает знающим людям хотя бы видимость неблокирующей задершки.  

Ну почему, обязательно,  видимость? На ЕСП можно создать несколько threads и тогда delay будет реально отдавать время другим потокам.

rkit
Offline
Зарегистрирован: 23.11.2016

Заставь дурака богу молиться.

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

asam пишет:

DetSimen пишет:

Понимаешь ли.  В стандартном delay(), унутре, есть вызов функции yield(), что дает знающим людям хотя бы видимость неблокирующей задершки.  

На ЕСП можно создать несколько threads и тогда delay будет реально отдавать время другим потокам.

Я в ESP "не селен". 

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

DetSimen пишет:

Я в ESP "не селен". 

Там на самом деле FreeRTOS крутится, а "ардуино" это просто отдельный поток. Но никто не запрещает прямо из него создавать другие.

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

DetSimen пишет:

Какая разность скока налили, 400 капель, или 402. 

Не скажи, 400 и 320 - разница есть.

Lictor
Offline
Зарегистрирован: 01.10.2015

Я конечно прошу пардона, что столь бесцеремонно прерываю разговор гуру, но будут ли комментарии к посту №12 или предложения, ссылки, намеки, как правильно организовать задержку?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Lictor пишет:

Я конечно прошу пардона, что столь бесцеремонно прерываю разговор гуру, но будут ли комментарии к посту №12 или предложения, ссылки, намеки, как правильно организовать задержку?

Так ещё раньше сказали - просто поставь delay и непарься. В данном случае принципиальной разницы нет.

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

Lictor пишет:

как правильно организовать задержку?

так вроде все уже ответили - код #12 полностью аналогичен строчке

delay(5)

так зачем усложнять?

Замена delay() через while() - типичная ошибка новичков, тупо зазубривших "делей = плохо", но абсолютно не понимающих, зачем вообще нужны миллис

 

В этом коде применение миллис абсолютно ничего не дает.

Lictor
Offline
Зарегистрирован: 01.10.2015

b707 пишет:

Lictor пишет:

как правильно организовать задержку?

так вроде все уже ответили - код #12 полностью аналогичен строчке

delay(5)

так зачем усложнять?

Замена delay() через while() - типичная ошибка новичков, тупо зазубривших "делей = плохо", но абсолютно не понимающих, зачем вообще нужны миллис

 

В этом коде применение миллис абсолютно ничего не дает.

 

Ок, понял. Благодарю за помощь.