Помогите с обвязкой ATtiny85

Здравствуйте, собираю простенький измерительный прибор на ATtiny85.
Помогите с обвязкой голой ATtiny85, сейчас при измерении батарейки показания на дисплее постоянно прыгают от 1.2 до 2.4, что мне нужно добавить из радиодеталей, что бы показания не прыгали?

Подключение такое.

В коде измеряю так:

#include <avr/io.h>
#include <util/delay.h>


#define PIN_A2   A2 
#define PIN_P1   3                                                                            //Назначаем пин P1


float R1_R4                           =  108.9;                                               //Сопротивление резистора R1 + R4 в кОм 98.9 + 10
float R2                              =  9.7;                                                 //Сопротивление резистора R2 в кОм 9.7

#define OLED_ADDR       0x78                  
#define OLED_CMD_MODE   0x00                  
#define OLED_DAT_MODE   0x40                  
#define DISPLAY_INIT_LEN   15  
 
#define I2C_SDA         PB0                   
#define I2C_SCL         PB1                  
 
#define I2C_SDA_HIGH()  DDRB &= ~(1<<I2C_SDA) 
#define I2C_SDA_LOW()   DDRB |=  (1<<I2C_SDA) 
#define I2C_SCL_HIGH()  DDRB &= ~(1<<I2C_SCL) 
#define I2C_SCL_LOW()   DDRB |=  (1<<I2C_SCL) 
 


uint16_t VOLT       = 0;
uint8_t DATA[5]   = {};



float DEFAULT_VOLT                    = 0;                                                    //Напряжение по умолчанию;
float  VOLT_DISPLAY                   = 0;                                                    //Объявляем переменную для хранения значения напряжения
uint16_t AVERAGE_VOLT                 = 0;
unsigned long TIME                    = 0;                                                    //Объявляем переменную таймера задержки измерений

#include "DISPLAY.h"
 
void setup() 
{
    DISPLAY_INIT();
    pinMode(PIN_P1, OUTPUT);                                                                    //Пин установлен на выход
    pinMode(PIN_A2, INPUT);                                                                     //Пин установлен на вход
    DEFAULT_VOLT                        = GET_DEFAULT_VOLT();                                   //Измеряем внутреннее напряжение
}

void loop()
{
  if (millis() - TIME <= 200)                                                                   //Добавляем задержку в 200 миллисекунд
    return;
  TIME = millis(); 
  //DEFAULT_VOLT = GET_DEFAULT_VOLT();


  DISPLAY_CLEAR();  
  VOLT_DISPLAY = analogRead(PIN_A2) * DEFAULT_VOLT / 1024 * (( R1_R4 + R2 ) / R2);              //Рассчитываем значение напряжения

  
  if (VOLT_DISPLAY >= 0.7)                                                                      //Если напряжение больше 0.7 вольт, значит плюс
  {
    if (digitalRead(PIN_P1) == HIGH)                                                            //Подаём прерывистый звуковой сигнал
      digitalWrite(PIN_P1, LOW);
    else
      digitalWrite(PIN_P1, HIGH);
  } 
  else if (VOLT_DISPLAY < 0.7 && VOLT_DISPLAY >= 0.3)                                           //Если напряжение в диапазоне от 0.3 до 0.7 вольт, значит масса
  {
    digitalWrite(PIN_P1, HIGH);                                                                 //Подаём непрерывистый звуковой сигнал
  }
  else if (VOLT_DISPLAY < 0.3)                                                                  //Если напряжение ниже 0.3 вольт
  {
    digitalWrite(PIN_P1, LOW);                                                                  //Отключаем звуковой сигнал
  }
    
  if (VOLT_DISPLAY >= 10)                                                                       //Напряжение больше 10 вольт
  {
    VOLT  = VOLT_DISPLAY * 100;                                                                 //Сдвигаем значение напряжение на 2 бита влево
  }
  else                                                                                          //Напряжение меньше 10 вольт
  {                                                                                  
    VOLT  = VOLT_DISPLAY * 1000;                                                                //Сдвигаем значение напряжение на 3 бита влево
  }
  if (VOLT_DISPLAY >= 0.7 || VOLT_DISPLAY < 0.3)                                                //Если напряжение больше 0.7 вольт и меньше 0.3 вольта
  {
    DATA[0]       = VOLT / 1000;   
    DATA[1]       = VOLT_DISPLAY >= 10 ? (VOLT % 1000) / 100 : 16;
    DATA[2]       = VOLT_DISPLAY >= 10 ? 16 : (VOLT % 1000) / 100;
    DATA[3]       = (VOLT % 100) / 10;
    DATA[4]       = VOLT % 10;                                           
    DISPLAY_PRINT(DATA); 
  }
  else                                                                                          //Иначе
  {
    DATA[0]       = 10;   
    DATA[1]       = 11;
    DATA[2]       = 12;
    DATA[3]       = 13;
    DISPLAY_PRINT(DATA);                                                                       //Отображаем на дисплее слово MASS
  }   
}


float GET_DEFAULT_VOLT() {                                                                      //Функция измеряет внутреннее напряжение Arduino
  long RESULT         = 0;                                                                      //Определяем переменную для получения результата.
  byte  COUNT_RESULT  = 100;                                                                    //Определяем сколько значений АЦП требуется получить для усреднения результата.
                                                                                                //Для Arduino Mega, Leonardo и Micro, сбрасываем бит «MUX5» регистра «ADCSRB», так как «MUX[5-0]» должно быть равно 011110 (см. регистр «ADMUX»).
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) //Устанавливаем биты регистра «ADMUX»: «REFS»=01 (ИОН=VCC), «ADLAR»=0 (выравнивание результата по правому краю), «MUX[4-0]»=11110 или «MUX[3-0]»=1110 (источником сигнала для АЦП является напряжение ИОН на 1,1 В).   
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif

  for(byte i=0; i<COUNT_RESULT; i++)                                                            //Получаем несколько значений АЦП
  {
    ADCSRA |= _BV(ADSC);                                                                        //Запускаем преобразования АЦП:Устанавливаем биты регистра «ADCSRA»: «ADEN»=1  (вкл АЦП), «ADSC» =1 (запускаем новое преобразование). 
    while (bit_is_set(ADCSRA, ADSC));                                                           //Получаем данные АЦП:
    uint8_t _LOW  = ADCL;
    uint8_t _HIGH = ADCH;
  
    RESULT += (_HIGH << 8) | _LOW;                                                              //Суммируем результат
  }
  RESULT /= COUNT_RESULT;                                                                       //Делим результат «RESULT» на «COUNT_RESULT», так как мы получили его «COUNT_RESULT» раз.    
  return (1.1f/RESULT) * 1024;                                                                  //Рассчитываем напряжение питания:  //  АЦП = (Uвх/Vcc)*1023. Напряжение Uвх мы брали с внутреннего ИОН на 1.1 В, значение которого возвращает функция analogSave_1V1(0).
}

То, что сразу в глаза бросается:

  1. Не хватает фильтра питания;
  2. Нет резисторов подтяжки на линии I2C (или они на модуле есть?)
  3. Если нужны точные измерения, то измерять надо в режиме REFS2-0 = 111 и обязательно ставить конденсатор на пин AREF (PB0)
  4. В программе, если нужны точные измерения, то это делается в режиме подавления шумов (“ADC Noise Reduction”), а не просто analogRead. Тогда МК спит во время измерени и шумы минимальны.

Зачем Вам два щупа таким образом выведенные я пока не понял, Вам виднее, захотите - объясните.

между щупами меряет, дифрежим аднака

Зачем Вам два щупа таким образом выведенные я пока не понял, Вам виднее, захотите - объясните.

@ua6em
между щупами меряет, дифрежим аднака

Всё верно.

  1. Не хватает фильтра питания;

Питание платы от аккумулятора, но добавил ещё два конденсатора, дроссель не стал.

  1. Нет резисторов подтяжки на линии I2C (или они на модуле есть?)

У модуля свой.

  1. Если нужны точные измерения, то измерять надо в режиме REFS2-0 = 111 и обязательно ставить конденсатор на пин AREF (PB0)

При установке конденсатора между ногой AREF (PB0) и минусом, модуль перестаёт включаться.
Как написать REFS2-0 = 111 в код, я так и не понял.

  1. В программе, если нужны точные измерения, то это делается в режиме подавления шумов (“ADC Noise Reduction”), а не просто analogRead. Тогда МК спит во время измерени и шумы минимальны.

Об этом я не знал, спасибо.

Спасибо за совет, сейчас в измерениях нет скачков, что сделал:

  1. Установка конденсаторов на PB3, PB4 и на вход питания.
  2. Перенос DISPLAY_CLEAR(); после analogRead.

Надеюсь, Вы не оставили при этом модуль подключённым к этом пину, как на схеме? Этот пин должен быть свободен - только конденсатор. Модуль к другому подключайте.

Ну, как бы, вот так,

ADMUX |= (1 << REFS2) | (1 << REFS1) | (1 << REFS0);

только это лишь вершина айсберга. При этом много чего меняется. Например, опорное становится 2,56 вольта. Это напряжение будет выведено на PB0 (его нельзя никак трогать ни в программе, ни в схеме - только конденсатор 0,1µF).

Т.е. по этому вопросу (а также про “ADC Noise Reduction”), Вам бы даташит хорошо почитать. Без этого никак.

2 лайка

Спасибо за ответ.