Attiny85 измерение напряжение от которого питается.

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
Ребят, подскажите. Я наткнулся на один нюанс. Возможно есть какая то хитрость или я, что то не правильно сделал.
Собрал я на attiny85 схему, по пинам:
- i2c oled аппаратные пины
- PB1 прерывание
- PB4 кнопка обычная
- PB3 пин на измерение напряжение с делителем.
Нюанс в измерение напряжения. Если измерять напряжение другого аккумулятора, то измеряет правильно, но как только пытаюсь измерить аккумулятор собственный, от которого attiny85 питается. нечего не получается. Я попробовал на дисплей вывести показания самого порта(A3 он же PB3) и правда показания самого аналогового пина не меняются если измерять питание от которому Тинька подключена. Подскажите, кто может сталкивался с подобным? На ардуино такой проблемы я не наблюдал, а с Тинькой, вот такая засада. Хочу, чтоб заряда аккумулятора от которого питается Тилька, показывала на дисплеи.
 

bort707

★★★★★★✩
21 Сен 2020
3,402
981
правда показания самого аналогового пина не меняются если измерять питание от которому Тинька подключена.
Конечно не меняются - ведь по умолчанию МК измеряет сигнал ОТНОСИТЕЛЬНО напряжения питания. Поэтому напряжение питания всегда будет давать максимальное значение ADC, если измерять без делителя.
Для измерения своего питания используют такой трюк - измеряют фиксированное напряжение относительно VCC и пересчитывают.
 
  • Лойс +1
Реакции: poty

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@ViDi79, Если мк питания напрямую от акб, то никакие делители не нужны, считать значение ацп можно так:
C++:
#include <util/delay.h>

uint16_t getADC(void) {
  ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0); //включаем ацп, режим однократного замера, пределитель 128
  ADMUX = (0x01 << MUX3) | (0x01 << MUX2); //установили vcc как опорное напряжение, подключили ИОН 1.1в ко входу ацп
  _delay_ms(2); //ждём пока ИОН "успокоится"
  ADCSRA |= (0x01 << ADSC); //запускаем преобразование ацп
  while (ADCSRA & (0x01 << ADSC)); //ждём пока преобразование ацп завершится
  return (uint16_t)ADCL | ((uint16_t)ADCH << 8); //возвращаем 10 бит результата преобразования ацп
}
Перевести значение ацп в напряжение так:
C++:
#define REFERENCE 1.10 //опорное напряжение, шаг регулировки 0.01в

float adcToVcc(uint16_t adc) {
  return (REFERENCE * 1024.0) / adc; //переводим "сырые" данные с ацп в напряжение питания
}
REFERENCE - это опорное напряжение, его значение нужно будет настроить так, чтоб отображаемое напряжение было равно измеренному физически(например мультиметру).
 
Изменено:
  • Лойс +1
Реакции: Bula и bort707

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
Спасибо, я понял, что делитель не нужен.
Ребят, а где можно почитать или посмотреть уроки по:
ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0);
ADMUX = (0x01 << MUX3) | (0x01 << MUX2);
ADCSRA |= (0x01 << ADSC);
?
Много раз видел в скечах, но не понимаю такой синтаксис. Кроме операторов которые в Уроках "Заметки Ардуинщика" не шиша не понятно.
 

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
C++:
#include <util/delay.h>

uint16_t getADC(void) {
  ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0);
  ADMUX = (0x01 << MUX3) | (0x01 << MUX2);
  _delay_ms(2);
  ADCSRA |= (0x01 << ADSC);
  while (ADCSRA & (0x01 << ADSC));
  return (uint16_t)ADCL | ((uint16_t)ADCH << 8);
}
C++:
#define REFERENCE 1.1

float adcToVcc(uint16_t adc) {
  return (REFERENCE * 1024.0) / adc;
}
Или можно попросить Вас подписать строчки, что они делают. Я понимаю, что это ответ, но не понимаю, что и за что отвечает, и как его встроить в свой скечь.
 

BOT_Zilla

★✩✩✩✩✩✩
1 Апр 2022
24
13
@ViDi79, тут все не сложно. Смысл первой строчки в том, чтобы включить определенные биты в ADCSRA
C++:
ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0);
ADEN, ADPS2, ADPS1, ADPS0 - это константы, которые определяют номера битов. Они заранее прописаны в IDE (пусть будут 7, 2, 1 и 0 соответственно. Значения взяты от фонаря, IDE знает, что туда подставить). Поэтому, если развернуть первые скобки, получится выражение 0х01<<7. Это побитовый сдвиг числа 1 влево на количество позиций, равное значению ADEN (т. е. 7). В результате получится число, в котором только бит ADEN будет равен 1, а остальные — 0 (в двоичном виде 10000000).
Теперь, если просчитать остальные скобки, получится выражение
C++:
ADCSRA = 10000000 | 00000100 | 00000010 | 00000001;
Операция | (побитовое ИЛИ) объединяет эти маски, в результате чего все указанные биты будут установлены в 1, а остальные — в 0. Как результат, ADCSRA примет значение 10000111.
 
  • Лойс +1
Реакции: Sergo_ST

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
Спасибо. По битам я понял. А ADCSRA и ADMUX это что?
 

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@ViDi79, Ничего сложного, просто вставляете следующую конструкцию в место, где нужно выводить значение напряжения(например вывод на дисплей или монитор порта):
C++:
adcToVcc(getADC())
Пс. Добавил комментарии в коде выше.
 

BOT_Zilla

★✩✩✩✩✩✩
1 Апр 2022
24
13
@ViDi79, придется читать букварь (он же datasheet) по этому микроконтроллеру, раз Вы задаете такие вопросы.
ADCSRA — это регистр управления и статуса АЦП. Этот регистр используется для включения АЦП, управления началом преобразования, настройки предделителя тактовой частоты и других функций.
ADMUX — это регистр выбора входного канала и опорного напряжения для АЦП. С помощью этого регистра можно выбрать, какой из аналоговых входов микроконтроллера будет преобразовываться в цифровое значение, а также задать опорное напряжение для АЦП.
 

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
Оформи код соответствующим тэгом, см. Правила
Если не будет возражений.

Собрал ИТОГовый вывод с Примером(опробовал, по подсказкам ребят)
Очень интересное решение, не какого лишнего обвеса для Тиньки не нужно. Для таких новичков как я, будет интересно.

К Attiny85 подключил дисплей OLED 0.66 по i2c и все.

C++:
#include <TinyWireM.h>
#include <Tiny4kOLED.h>

// Начало Самого кода написанный ребятами выше, для измерение attiny85 собственного источника питания
#include <util/delay.h>
#define REFERENCE 1.1
uint16_t getADC(void) {
  ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0);
  ADMUX = (0x01 << MUX3) | (0x01 << MUX2);
  _delay_ms(2);
  ADCSRA |= (0x01 << ADSC);
  while (ADCSRA & (0x01 << ADSC));
  return (uint16_t)ADCL | ((uint16_t)ADCH << 8);
}
float adcToVcc(uint16_t adc) {
  return (REFERENCE * 1024.0) / adc;
}
// конец кода описанный ребятами выше.

void setup() {
  oled.begin(128, 64, sizeof(tiny4koled_init_128x64br), tiny4koled_init_128x64br);
  oled.clear();
  oled.on();
}
void loop() {
  oled.setFont(FONT6X8);
  oled.setCursor(45, 22);
  oled.print(adcToVcc(getADC())); // adcToVcc(getADC()) это показания измерения своей источника питания.
  delay(1000);
}
Спасибо, сам бы я долго по инету рыскал решение.
 
Изменено:

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
32
2
Ребят подскажите, продолжение к этой теме.
На "+" повесил конденсатор с диодом, чтоб по отключению питания в еепром записывать. Еще в схеме присутствует прерывание (ИК датчик препятствия). Сравниваю - если adcToVcc(getADC()) < 3.0 Вольт, то в ЕЕПРОМ записывает, так сказать при падении напряжения. Только есть проблемка, ложное срабатывание при этом сравнении проходит. Методом проб и ошибок выяснил. Посоветуйте, может на прерывание проблема? Частенько в момент срабатывание ИК датчика, проходит просадка? Так вроде конденсатор электролитический должен вытягивать. В общем тупик. Если триггер не прописывать в скеч, то еепром быстро загнется. Без триггера он как не в себе перезаписывает в еепром по ложным срабатыванием в сравнении.
 

artemsaf

✩✩✩✩✩✩✩
8 Мар 2025
12
0
Здравствуйте подскажите, заработает ли этот код на attiny13a или надо что изменять
C++:
#include <util/delay.h>



uint16_t getADC(void) {

  ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0); //включаем ацп, режим однократного замера, пределитель 128

  ADMUX = (0x01 << MUX3) | (0x01 << MUX2); //установили vcc как опорное напряжение, подключили ИОН 1.1в ко входу ацп

  _delay_ms(2); //ждём пока ИОН "успокоится"

  ADCSRA |= (0x01 << ADSC); //запускаем преобразование ацп

  while (ADCSRA & (0x01 << ADSC)); //ждём пока преобразование ацп завершится

  return (uint16_t)ADCL | ((uint16_t)ADCH << 8); //возвращаем 10 бит результата преобразования ацп

}

#define REFERENCE 1.1



float adcToVcc(uint16_t adc) {

  return (REFERENCE * 1024.0) / adc;

}



#define REFERENCE 1.1



float adcToVcc(uint16_t adc) {

  return (REFERENCE * 1024.0) / adc;

}
 

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@artemsaf, На Attiny13A нельзя измерить напряжение питания подобным образом тк внутреннее строение цепей АЦП не позволяет подключить встроенный ИОН к мультиплексатору АЦП.
 

artemsaf

✩✩✩✩✩✩✩
8 Мар 2025
12
0
А есть ли вариант на attiny13 измерения напряжения батарейки от которой она сама и питается ? Например в датчике , что бы знать когда пора менять ?
 

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@artemsaf, Только с внешней обвязкой и использованием 1-2 портов мк в зависимости от выбранного исполнения.
Можете поискать в интернете про резистивный делитель или определение разряда батареи с помощью стабилитрона.
 

artemsaf

✩✩✩✩✩✩✩
8 Мар 2025
12
0
Проблема не с делителем , проблема с кодом . ок, спасибо, буду искать , раз возможно.
 

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@artemsaf, Так код будет напрямую зависеть от выбранного подхода и задач которые он должен решать, тут нет каких-то универсальных решений...
Если требуется знать точное напряжение - то нужно использовать ацп, если достаточно самого факта что батарея разряжена - можно использовать просто встроенный компаратор...
Сначала вам нужно определиться с железной частью.

Ну и имейте ввиду если будете измерять с помощью АЦП, то встроенный ИОН имеет напряжение 1.1в, поэтому подаваемый сигнал должен быть в диапазоне от 0 до 1.1в.
 

artemsaf

✩✩✩✩✩✩✩
8 Мар 2025
12
0
Через АЦП батарейка 3v cr2032.
Задача считывать её напряжение, что бы при ниже 2.7v отправлять оповещение.
С отправкой оповещения разобрался.
С делителем тоже.
Счас исчу как сделать считывание.
 

Bruzzer

★★★★✩✩✩
23 Май 2020
774
235
Если будете еще спрашивать, то спрашивайте в теме про attiny13. Если такой темы нет, то создайте новую.
 
  • Лойс +1
Реакции: Sergo_ST

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@artemsaf, Если вы пишете в Ардуино ИДЕ и используете minicore в качестве ядра, то достаточно добавить в setup:
Код:
analogReference(INTERNAL1V1);
Прочитать значение ацп можно с помощью этой функции:
Код:
analogRead(пин);
На выходе вы получите значение АЦП от 0 до 1023, где 0 - 0в, а 1023 - ~1.1в.
 

artemsaf

✩✩✩✩✩✩✩
8 Мар 2025
12
0
В Ардуино, но на С . Иначе не влезет.
Наверно аналог на С этих команд можно в самой библиотеке найти.
 

Sergo_ST

★★★★★★✩
15 Мар 2020
1,206
922
@poty, Ну да, можно использовать в качестве опорного, но к мультиплексатору(INPUT MUX) увы его невозможно не подключить...
Нам же что бы узнать напряжение питания нужно измерить ИОН относительно VCC)