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

ViDi79

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

bort707

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

Sergo_ST

★★★★★★✩
15 Мар 2020
962
826
@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
27
0
Спасибо, я понял, что делитель не нужен.
Ребят, а где можно почитать или посмотреть уроки по:
ADCSRA = (0x01 << ADEN) | (0x01 << ADPS2) | (0x01 << ADPS1) | (0x01 << ADPS0);
ADMUX = (0x01 << MUX3) | (0x01 << MUX2);
ADCSRA |= (0x01 << ADSC);
?
Много раз видел в скечах, но не понимаю такой синтаксис. Кроме операторов которые в Уроках "Заметки Ардуинщика" не шиша не понятно.
 

ViDi79

✩✩✩✩✩✩✩
1 Окт 2023
27
0
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
11
8
@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
27
0
Спасибо. По битам я понял. А ADCSRA и ADMUX это что?
 

Sergo_ST

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

BOT_Zilla

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

ViDi79

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

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

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