Многоканальный вольтметр c LCD дисплеем на ATmega 8.

a5021
Offline
Зарегистрирован: 07.07.2013

Мне кажется вы слишком смело обошлись с разрядностью АЦП. На 64-х семплах там по хорошему если и удастся вытащить один разряд, то надо очень пристально смотреть, что получится с точность. Четыре разряда в этом случае это сильно круто.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Остановился на 256 семплах:

unsigned int analogReadOversampled(uint8_t analogChannel)  {
  unsigned long aSum = 0;   // the sum of all analog readings
  for(int i = 0; i < 256; i++) 
    aSum += read_adc(analogChannel); // read and sum 16 ADС probes
  return aSum >> 4;   // ..
}

В целом меня устраивает, лучше чем то что у меня было до знакомства с оверсемплингом.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman, и как теперь с девиацией показаний, не гуляют?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Скорость отправки в юарт упала до  8 раз в секунду а показания практически не гуляют:

Оверсемплинг не панацея конечно, но в целом с шумами справляется на ура + повышает немного точность измерений, может позже выкладу графики с оверсемплингом и без.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman, да,  на картинке выглядит весьма неплохо.  Вообще хорошо бы многооборотным резистором плавно менять напряжение, и смотреть как меняется уровень девиации, возможно он существенно вырастет, если напряжение на входе ацп попадёт точно между порогов. О точности измерения тут судить сложно, по идее от усреднения вырастает относительная точность, а фактическая всё равно будет соответствовать двум отсчётам, т.к. определяется нелинейностью других параметров, об этом было написано в даташите.  Т.е. на 15 вольтовом диапазоне это будет 0,03в. Это легко проверить,  если например на  12-вольтах откалибровать по прецизионному вольтметру, то на других напряжениях значение уже уйдёт от эталонного прибора, и чем дальше от напряжения калибровки, тем  разница будет больше. Но нам главное что бы не ушло более, чем на 2 отсчёта ацп :)

 

a5021
Offline
Зарегистрирован: 07.07.2013

HWman пишет:

Скорость отправки в юарт упала до  8 раз в секунду а показания практически не гуляют

А почему вы так держитесь за эту скорость измерений? Ширпотребные мультиметры вообще чуть ни раз в секунду показания дисплея меняют и никто не жалуется, что ими невозможно пользоваться.

dimax пишет:

О точности измерения тут судить сложно, по идее от усреднения вырастает относительная точность, а фактическая всё равно будет соответствовать двум отсчётам, т.к. определяется нелинейностью других параметров, об этом было написано в даташите.  

За повышением точности (разрядности АЦП) при оверсемплинге стоит весьма серьезная математика, т.ч. сомневаться в этом моветон.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Потенциометром не получится нормально поиграться, сделал немного по другому, подал на 12 В аккум небольшую нагрузку, и убрал, картина вот такая:

Потом подал опять небольшую нагрузку:

Кстати, чтобы как-то убрать мандраж графика я решил отправлять по 4 цифры после комы, я так понимаю функция Serial.print(); усредняет показания, из за этого получается лишний шум на графиках, думаю та же история с функцией lcd.print(); получается лишний мандраж на LCD дисплее, кстати у меня частота обновлений 6-8 Гц, выглядит довольно прикольно, если напряжение не быстро меняется тогда почти ничего не "плывёт".
ЗЫ делитель состоит из резистора 10 кОм(шунтирующий АЦП) и 30 кОм, коэффициент получается 4, примерно на 4-х вольтах уже начинает влиять стабилитрон, наличие которого для меня обязательно, так что область 4-5 В на АЦП я буду тупо игнорировать, леньки что-то править программно.
 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a5021, Вы наверное не правильно меня поняли.  Точность -понятие многоплановое. То, что вы сейчас подразумеваете под точностью оверсемплинга, -это разрешающая способность, которую можно восстановить до чистых 10 или даже виртуальных 11 бит.  А есть ещё абсолютная точность, которая зависит от множества слагаемых, в том числе и от разрешающей способности. Но увеличением разрешающей способности абсолютную точность уже не изменить, т.к. она ограничена другими факторами, о чём я и пытался сказать.

a5021
Offline
Зарегистрирован: 07.07.2013

Предположу, что в этот раз я вас все-таки понял. :) Естественно, никакой точности не будет, если питание не стабильное, сигнал грязный, а измерения организованы не правильно. Например, топикстартер говорит, что измеряет напряжение с делителя 30 и 10 килоом, что лично у меня вызывает серьезные нарекания, т.к. входное сопротивление АЦП оказывается меньше выходного сопротивления делителя. В этом случае может возникнуть ситуация, когда входные емкости УВХ АЦП оказываются недозаряженными (через 30 килоом заряжаться им приходится дольше положенного, т.к. по паспорту атмега должна проводить замеры с источника выходным сопротивлением не более 10 килоом), если циклы преобразования следуют один за одним без перерыва. Другими словами, изеряется не напряжение в точке измерений, а напряжение до которого успели зарядиться емкости УВХ. Если в делитель поставить еще более высокоомные номиналы, но с сохранением пропорций деления, то показания замеров упадут еще, в то время, как реальные напряжения останутся те же самые. Эта ситуация как раз и похожа на то, что называют неправильной организацией измерений.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

На данный момент у меня вот так:

А я ещё хотел поднять номинал резистора R1.1 на 10 кОм...

Чёрт, с каждым днём я понимаю что я ничего не знаю ни в микроконтроллерах ни в электротехнике...

Я думал что подобрав номиналы резисторов таким образом я сделаю так чтобы делитель практически ничего не кушал от измеряемого источника, ну пару сотен микроампер ни в счёт.

Короче, пошёл гуглить на тему АЦП и резистивных делителей напряжения

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Стоп, может это всё крайности и для моего проекта, а именно 4-х канальный не сложный вольтметр с лсд дисплеем, не стоит сильно заморачиваться над такими вещами, ведь у меги 10-ти битный АЦП, внешний АЦП использовать не планирую, точность +/- 0,05 В вполне устраивает.

Есть задумка сделать вольтметр на уровень поточней, но это уже другой проект, там точно буду юзать внешний АЦП.

a5021
Offline
Зарегистрирован: 07.07.2013

Китайцы делают дешевые четырехразрядные вольтметры с весьма неплохой точностью без всяких внешних АЦП. Весь вольтметр состоит из LDO, МК, индикатора и кучки резисторов. 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

a5021, теперь кажется я вас не понял :) Как понять фразу "входное сопротивление АЦП оказывается меньше выходного сопротивления делителя" ?  Выходной импенданс делителя как и рекомендует даташит 10 ком.  Второй резистор ограничивает ток, до такого уровня, при котором входное напряжение на ацп не будет превышать максимально допустимого. Всё как положено, а как иначе по вашему  должно быть?

a5021
Offline
Зарегистрирован: 07.07.2013

Выходное сопротивление данного делителя равно 7.5килоом. Я насчитал тридцать, вы десять, в общем, оба хороши. :) Насчет тридцати меня что-то перемкнуло, т.ч. извиняйте за дезу.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Нарисовал парочку символов:

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

Снял видео роботы http://www.youtube.com/watch?v=4qGu-MEXd1E .

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

a5021 пишет:

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

int analogReadOversampled(int analogChannel)  {
 
  int aSum = 0;   // the sum of all analog readings
  for (int i = 0; i < 16; i++) 
    aSum += analogRead(analogChannel) // read and sum 16 ADС probes
  return aSum >>= 4;   // equivalent of division by 16
}

Но это еще не вся "магия". Если брать число замеров кратное "4 в степени х" (4, 16, 64, 256...), то вместе с усреднением можно получить и увеличение разрядности АЦП, а следовательно, точности, если "недосдвинуть" полученную сумму вправо. Так, если в предыдущем примере сдвинуть переменную aSum не на 4, а на 3, то результатом будет представление среднего от 16 измерений в одиннадцати-битном разрешении. Если сдвинуть только на два, то, соответственно, в двенадцати-битном.

Шестнадцать семплов это все-таки маловато для качественного усреднения, а по сему лучше не зажиматься особо, а брать по 256 или 1024, если конечно измеряемый сигнал не является быстро меняющимся. По своему опыту знаю, что 256 семплов при последущем приведении к 12 битам дает и приемлемое усреднение и отчетливое увеличение точности.

 

http://chipenable.ru/index.php/programming-avr/item/141-avr121-oversampl...

http://chipenable.ru/index.php/programming-avr/item/142-avr121-oversampl...

Случайно не Вы автор этих статей? 

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Поставил себе цель уменьшить энергопотребление до минимума на сколько мне дают знания, сделал что если напряжение на всех щупах вольтметра напряжение меньше 0.01 В тогда МК засыпает на 2 сек при этом подсветка лсд тушится для экономии энергии, при этом сбрасывается когда просыпается, не сбрасываться не получилось, вроде как мега8-я не поддерживает прерывания по ватчдогу.

Листав даташит по дисплею я увидел таблицу:

Питание логики дисплея жрёт целый миллиампер, как я понимаю даже когда МК спит крепким сном лсд жрёт свой милиампер, идея состоит вот в чем, повесить на свободный порт(а их у меня ещё достаточно) МК питалово дисплея(ножку VDD) и перед сном убирать лог. 1 с порта, тем самым полностью отключая дисплей. Подключать к порту планирую через резистор 320 Ом, других поменьше номиналом просто нету.

Собственно вопрос, стоит ли овчинка выделки? Или это ещё то извращение?

 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman, у вас, прошу меня простить многое в этом проекте изврат :) Насколько я помню ваша задумка -вольтметр для комповых БП. Если он питается сам от батарейки, то в сне контроллера и дисплея есть небольшой смысл. Хотя если измерять ничего не нужно, то отчего же просто не выключить питание. Питание для лсд (без подсветки) не нужно через резистор давать, т.к. ограничивать ток тут не зачем, если вы его поставите, то напряжение на самом лсд уже не будет 5 вольт, возможно оно снизиться менее 4, 5в, и дисплей начнёт глючить. Вообще  технически правильно ставить для таких случаев ключевой транзистор.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Энергопотребление это просто каприз, хочется и всё.

Ну может захочу шагнуть дальше ПК БП, я и уже про трёх канальный вольт-амперметр подумываю... 

Вчера игрался с шунтом, как игрался, взал метр медного провода, скрутил спиралькой чтобы меньше места занимал, подал через него ток, где-то 4-5 А и замерил падение напряжения - 25 мВ, тут сразу прикинул если замерять падение напряжения относительно ИОНа то получится точность 0.5 А что меня бы вполне устраивало бы, можно конечно сварганить на ОУ или купить готовый модуль, но я ленивый бедный студент, проволочки несколько метров у себя найти думаю проблемы не будет. Подумывал и про то чтобы подать напряжение скажем в 1 В при помощи резистивного делителя напряжения на ножку AREF и относительно этого напряжения делать измерение тока, может и даже сделать ниже напряжение если можно.

Но моего 1602 для всего этого не хватит чтобы выводить всю информацию за раз, можно и каждую секунду менять переменные в лсд.принт но хотелось чтобы всё сразу выводилось, посмотрев цены на 2004 - дороговато однако, но как говорится "если захотеть то можно в космос полететь".

Идей много но всё делать как-то леньки.

Насчёт транзистора тоже думал над ним, есть у меня парочка 2n7000 как раз должно хватить.
Самый изврат это в схеме, я в делителе напряжения заменил R1.1 на 10 кОм а подстроечник теперь у меня на 100 кОм, других многооборотистых не нашлось, зато имею широкий диапазон напряжений, а имено при измерениях относительно ИОНа 0-30 В максимум что можно настроить и если измерять относительно напр. питания 0-45В, есть смысл сделать автоматическое переключение ИОН, питалово, AREF для точных более измерений, но тут подумать немного что и как, в воскресенье убил целый день на это и так толком ничего не получилось, потом рассердился и по удалял все наработки нафиг, нужно начать сначала, порой это оптимальное решение.

Вот код который есть на сей момент:

#include <avr/io.h>
#include <avr/wdt.h> // здесь организована работа с ватчдогом
#include <avr/sleep.h> // здесь описаны режимы сна
#include <avr/power.h>
#include <LiquidCrystal.h>

#define interval 125 // обновление дисплея мс 

byte v1[8] = // Символ v1
{
  B10010,
  B10110,
  B10010,
  B10010,
  B10111,
  B10000,
  B01010,
  B00100,
};

byte v2[8] = // Символ v2
{
  B10111,
  B10001,
  B10111,
  B10100,
  B10111,
  B10000,
  B01010,
  B00100,
};

byte v3[8] = // Символ v3
{
  B10111,
  B10001,
  B10111,
  B10001,
  B10111,
  B10000,
  B01010,
  B00100,
};

byte v4[8] = // Символ v4
{
  B10101,
  B10101,
  B10111,
  B10001,
  B10001,
  B10000,
  B01010,
  B00100,
};

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

/*
  LCD:
 LCD RS - pin 7(PD7)
 LCD Enable - pin 8(PB0)
 LCD D4 - pin 9(PB1)
 LCD D5 - pin 10(PB2)
 LCD D6 - pin 11(PB3)
 LCD D7 - pin 12(PB4)
 LCD R/W - GND
 */

unsigned long previousMillis = 0;
boolean state;

void setup() { 

  lcd.createChar(1, v1); // создадим наши символы
  lcd.createChar(2, v2);
  lcd.createChar(3, v3);
  lcd.createChar(4, v4);

  adc_init();
  pinMode(13, OUTPUT); // подсветка LCD 
  digitalWrite(13, HIGH); // привет
  Serial.begin(115200); // у софта по умолчанию скорость 115200
  lcd.begin(16, 2);

  /*lcd.print("Multichannel"); // ...
   lcd.setCursor(0,1);
   lcd.print("Voltmeter v1.3"); // ...
   delay(3000);*/

}

void loop() {

  //////////////////////////////////////////////////////

  wdt_enable(WDTO_2S); // защита от зависаний МК, если не сбросить ватчдог через 2 сек МК уйдёт в ресет

  //////////////////////////////////////////////////////

  float voltage1 = analogReadOversampled(5) * (20.00 / 8184.00); // преобразуем результат
  float voltage2 = analogReadOversampled(4) * (20.00 / 8184.00); //
  float voltage3 = analogReadOversampled(3) * (20.00 / 8184.00);
  float voltage4 = analogReadOversampled(2) * (20.00 / 8184.00); //

  //////////////////////////////////////////////////////

  if(voltage1 > 0.01 || voltage2 > 0.01 || voltage3 > 0.01 || voltage4 > 0.01){ // вкл. подсветку если напр. больше 0.01 В хотя бы на 1 канале
    digitalWrite(13, HIGH);

    //////////////////////////////////////////////////////

    Serial.print(voltage1,4);
    Serial.print(",");
    Serial.print(voltage2,4);
    Serial.print(",");
    Serial.print(voltage3,4);
    Serial.print(",");
    Serial.print(voltage4,4);
    Serial.println();

    //////////////////////////////////////////////////////

    voltage1 = (int(voltage1*100))/100.00;
    voltage2 = (int(voltage2*100))/100.00;
    voltage3 = (int(voltage3*100))/100.00;
    voltage4 = (int(voltage4*100))/100.00;

    //////////////////////////////////////////////////////

    unsigned long currentMillis = millis(); 
    if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;
      //lcd.clear(); // бесит, можно обойтись и без
      lcd.setCursor(0,0);
      lcd.write(1);//lcd.print("V1=");
      lcd.print("=");
      if(voltage1 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage1);
      }
      lcd.print(" "); // нужно для того чтобы обходится без lcd.clear()
      lcd.setCursor(9,0);
      lcd.write(3);//lcd.print(" V3=");
      lcd.print("=");
      if(voltage3 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage3);
        lcd.print(" "); 
      }

      lcd.setCursor(0,1);
      lcd.write(2); //lcd.print("V2="); 
      lcd.print("=");
      if(voltage2 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage2);
        lcd.print("  ");
      }
      lcd.setCursor(9,1);
      lcd.write(4); //lcd.print(" V4=");
      lcd.print("=");
      if(voltage4 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage4);
        lcd.print(" "); 
      }
      wdt_reset(); // сбросим вачдог чтобы не сработала защита от зависаний
    }
  }
  else{
    Serial.println("sleep");
    delay(7); // для того чтобы адекватно отправлять Sleep
    digitalWrite(13, LOW);
    //wdt_reset(); // сбросим на всякий случай
    system_sleep_2S();
  }
}

//////////////////////////////////////////////////////

void adc_init(void){
  ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0));    //16Mhz/128 = 125Khz the ADC reference clock
  //ADMUX |= (1<<REFS0)|(1<<REFS1);                //(1<<REFS0); - 5 V, (1<<REFS0)|(1<<REFS1); - 2.56V
  ADCSRA |= (1<<ADEN);                //Turn on ADC
  ADCSRA |= (1<<ADSC);                //Do an initial conversion because this one is the slowest and to ensure that everything is up and running
}

uint16_t read_adc(uint8_t channel){
  ADMUX |= (1<<REFS0)|(1<<REFS1);   //(1<<REFS0); - 5 V, (1<<REFS0)|(1<<REFS1); - 2.56V
  ADMUX &= 0xF0;                    //Clear the older channel that was read
  ADMUX |= channel;                //Defines the new ADC channel to be read
  ADCSRA |= (1<<ADSC);                //Starts a new conversion
  while(ADCSRA & (1<<ADSC));            //Wait until the conversion is done
  return ADCW;                    //Returns the ADC value of the chosen channel
  ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
}

//////////////////////////////////////////////////////


void system_sleep_2S(){
  wdt_reset(); // сброс
  wdt_enable(WDTO_2S); // разрешение ватчдога раз в 2с
  //WDTCR |= _BV(WDE); // разрешение прерываний по ватчдогу (иначе будет резет)!
  //sei(); // разрешение прерывания
  ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // если спать - то на полную
  while(1) {
    sleep_enable(); // разрешаем сон
    sleep_cpu(); // спать!
  }
}

//////////////////////////////////////////////////////

unsigned int analogReadOversampled(byte analogChannel)  {
  unsigned long aSum = 0;   // the sum of all analog readings
  for(unsigned int i = 256; i > 0; i--){ 
    aSum += read_adc(analogChannel); // read and sum 16 ADС probes
  }
  return aSum >> 5;   // ..
  noise_generator(); // генерим шумы быстро мигая подсветкой
}

//////////////////////////////////////////////////////

void noise_generator(){ // для оверсемплинга
  state = !state; 
  digitalWrite(13, state);
}

//////////////////////////////////////////////////////

Посмотрите, может чего подскажете, мне очень трудно переводить код с процесинга на Сишный изза того что я упорно не хочу разбираться в регистрах микроконтроллера, на процесинге всё просто а тут он сколько кода.

Хочется сделать так чтобы можно было переключаться 5В, ИОН, AREF непосредственно при работе МК а не в коде, постоянно перепрошивая МК.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman,

по поводу измерения тока - сейчас есть множество вариантов как это осуществить. Самый простой вариант  собрать на микрухе ACS712/713, у неё аналоговый выход, её плюс -это гениальная простота и гальваническая развязка от измеряемого сигнала. Но минус в том, что показания будут прыгать от каждого чиха, влияет на них всё, вплоть до фазы луны. Но это реально самый лёгкий вариант.  Остальные варианты относятся к "усилителям сигнала шунта". Есть очень вкусные микрухи, у них почти нет обвязки кроме самого шунта, и они выдают результат сразу в цифре по I2C. Есть микры попроще, тоже с минимумом обвязки, но выдают аналоговый сигнал. Самый сложный способ -это собирать усилитель сигнала шунта самому, без специализированных чипов. Я кстати надавно делал. Нужен операционник, мощный мосфет и кучка рассыпухи. С вашей ленью не могу предложить ничего, кроме варианта №1.

по поводу вывода на дисплей. В принципе в инете гуляет огромная куча библиотек под LCD дисплеи от мобильников. От той-же нокии 3310 например, который стоит сущие копейки. И влезет в него информации больше чем на lcd1602. И свой символ любой нарисовать можно.

по поводу делителя -как я уже и писал, я не сторонник подстроечных резисторов. Всё нужно подстраивать програмно. Я например писал функцию подстройки множителей для перевода в вольты, работающюю через енкодер. Можно было в реальном времени откалибровать измерение на любом удобном диапазоне.  Если необходимо иметь очень широкий диапазон измеряемых напряжений, то можно сделать автоматический переключатель диапазонов. Те-же 2n7000 наверное подойдут. Суть такая -котроллер смотрит за значением аналогреад, и если оно превышает скажем 1000 попугаев -то даёт на один из своих выходов сигнал, по которому транзистор включает дополнительный резистор в делителе. Теперь контроллер учитывает единицу на выходе как флаг диапазона, и при снижении значения скажем менее 100 && flag он отключает выход транзистора, и тот возвращает цепь для первого диапазона. Всё очень просто. Можно конечно переключать и ИОН между 5 вольт и внутренним для тех-же целей,  но поскольку я не сторонник делать analogReference от напряжения питания 5в (применительно к вольтметру) , то не могу такое посоветывать.

И по последнему пункту -не совсем понял, что значит с процессинга на Сишный. Вообще догадываюсь, т.к. иногда почему-то процессингом называют всё подряд,  но это просто язык визуализации на яве, он бы вам подошёл, если бы вы захотели выводить информацию не на lcd, а в комп. Вот на процессинге можно нарисовать любую "морду" прибора, и выводить туда столько данных, сколько нужно. Но тут у вас всё на СИ, и на спец функциях ардуино. Ваш скетч мне не везде понятен, например какую цель вы преследовали создавая функцию adc_init, это ровно тоже самое, что делает команда analogRead. В функции system_sleep можно ацп не отключать, он отключается в adc_init Больше всего удивил noise_generator, это лишнее. В чипе атмега столько шума, что для работы алгоритма передискретизации его даже больше, чем нужно.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

adc_init() и read_adc() нашёл в сети. После того как я добавил керамику к ножке AREF и земле, то  шуми получились такие как у Вас +/- 1, изредка 2.  

Я думал про дисплей от 3310, он маленький, как-то мне не нравится сам по себе, может я сильно консервативен в этом плане.

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

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

На даный момент скетч весит 6 492 байт (из 7 680 байт максимум), хочется максимально приблизится к 6 кб. Нужно оставить какой-то простор для будущих задумок.

ЗЫ Так как мегу шью по юарту то загружчик юзать обязательно.

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman, хоть это и допускается, но я бы всё таки не рекомендовал называть процессингом то, что вы видите в скетче ардуино IDE. Это чистейший язык СИ, и специфические ардуиновские команды -это самые обычные функции, просто они спрятаны от пользователя подальше. Например, давая команду analogRead или Write вы обращаетесь к файлу ..arduino\hardware\arduino\cores\arduino\wiring_analog.c в котором физически расписана эта функция. Итд, а процессингом лучше называть вот это http://habrahabr.ru/post/58314/. Просто иначе, когда человек говорит "процессинг" нужно по каким-то дополнительным сведениям догадываться о чём речь, о скетче для микроконтроллера, или о скетче для визуализации.

Можно конечно написать чтение аналогового входа без помощи функции, но смысла нет, если конечно не нужно задействовать какую-то недоступную особенность. Делая вольтметр на 16 мегагерцовом процессоре о скорости можно вообще забыть, даже на одном мегагерце он будет успевать всё делать, включая оверсемплинг.  Так что оптимизировать какие-то команды можно, но чисто из интереса, т.к. практической пользы в данном случае не будет. Не советую использовать что-то взятое из сети, без ясного понимания для чего оно нужно. И по поводу размера хекса -аналогично. Может в этом и есть какой-то спортивный интерес, но давиться в 8 килобайтах, когда проще простого поставить контроллер на 16 или 32 килобайта -какой смысл? Тем более вы собираетесь делать измерение тока, куда это всё потом втискивать? И по дисплею - не нравится от3310, так других куча, с огромным экраном. Правда это будет стоить уже не 30 рублей, и возможно примеры работы и библиотеки придётся хорошенько поискать.

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Достал плату с коробочки в очередной раз поиграться, и тут возникла мысль, а что если максимальное напряжение не будет зашито намертво а записано в еепром?

Ну и сделать какое-то секретное меню, попасть в которое реально только если подать лог. 1 на какой-то порт при старте, дальше потенциометром на нужном порту АЦП выставить желаемое напряжение,тоесть максимум измерения, чтобы не было зашито как в меня 20 В а изменять эту величину скажем от 5 В до 260 с шагом в 1 В на значение которое возвратит АЦП, на дисплее будет отображаться максимальное напряжение на каждом порту,  например: 

V1=260  V2=25
V3=15  V4=110

Каждое значение - содержимое ячейки еепрома + 5. Потом опять подаим лог. 1 на порт, МК примет настройки и перезагрузиться...
Если заданное на порту напряжение будет выше 20 В тогда выводимое число на лсд будет округляться до десятых, если > 200 В тогда до целого числа.

Потом можно будет рассчитать делитель на нужное напряжение например тут, задать нужное напряжение вышеописанным способом
и получиться уже что-то полезное а не просто какой-то проектик HWman'а.  

Главное чтобы на всё памяти хватило, и желания :)

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

HWman, можно просто сделать вольтметр с автоматически переключаемыми диапазонами. Суть в том, что контроллер, если считанное аналоговое значение  мало, отключает релюшками резисторы делителя до тех пор, пока не дотянет до возможного макимума.

Кстати номиналы делителя можно рассчитывать в уме за 1 секунду, без онлайн-калькуляторов.  Очень простая методика. Вычитаете из максимального напряжения опорное.  Например рассчитываем под 25 вольт при референс 5 вольт. 25-5=20. Результат= резисторы 20к и 5 к. В уме сразу можно менять, пропорции, например 40к и 10 к.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Вариантов реализации масса, всё упирается в мою лень и не компетентность. 

Mikhail_Sakh
Offline
Зарегистрирован: 03.04.2014

dimax пишет:

Есть очень вкусные микрухи, у них почти нет обвязки кроме самого шунта, и они выдают результат сразу в цифре по I2C. Есть микры попроще, тоже с минимумом обвязки, но выдают аналоговый сигнал. 

Интересуют такие микры. Подскажите пожалуйста. 

dimax
dimax аватар
Онлайн
Зарегистрирован: 25.12.2013

Mikhail_Sakh пишет:

dimax пишет:

Есть очень вкусные микрухи, у них почти нет обвязки кроме самого шунта, и они выдают результат сразу в цифре по I2C. Есть микры попроще, тоже с минимумом обвязки, но выдают аналоговый сигнал. 

Интересуют такие микры. Подскажите пожалуйста. 

Ну вот например  от максимыча http://www.maximintegrated.com/en/products/analog/amplifiers/current-sen...

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

dimax пишет:

HWman пишет:

Как считаете, сколько  раз нужно считать значение АЦП  чтобы нормально усреднить?

Вообще выбор метода усреднения, это огромная тема для дискуссий. По моему мнению усреднять нужно тогда, когда необходимо вывести точность измерения до цены одного отсчёта. Если у вас полная шкала будет 20 вольт, то один отсчёт будет примерно 20 милливольт. Как я понял вам такая точность не нужна, следовательно лучше не усреднять, а убирать избыточность. Проще всего это сделать так:

int x,  value=0;
void loop {
x = analogRead(A0);
if (x >value+1 || x<value-1) value=x ;

}

И дальнейшее преобразование в вольты делать из value. По поводу внутреннего ион меги8 -были мнения, что он слабоват.

Спасибо ещё раз, вот что получилось в итоге:

 

#define Noise_level 1 // фильтрация +/- 1 LSB
 
unsigned int sensor[3]; // буфер для записи предыдущего результата
unsigned int filter_sensor[3]; // отфильтрованное значения
 
void setup() {
  Serial.begin(9600);
}
 
void loop() {
  // make a string for assembling the data to log:
  String dataString = "";
  // read three sensors and append to the string:
  for (int analogPin = 0; analogPin < 3; analogPin++) {
    sensor[analogPin] = analogReadOversampled(analogPin);
 
    if(filter_sensor[analogPin] > sensor[analogPin] + Noise_level || // если предыдущее значение +/- 1 LSB
    filter_sensor[analogPin] < sensor[analogPin] - Noise_level){
      filter_sensor[analogPin] = sensor[analogPin];  // то выводим только предыдущее значение
    }
 
    dataString += String(filter_sensor[analogPin] / 2); // превращаем 11 бит в 10
    if (analogPin < 2) {
      dataString += ",";
    }
    else{
      dataString += ",";  
      dataString += (millis() / 1000);
    }
  }
 
  Serial.println(dataString);
}
 
//////////////////////////////////////////////////////
 
unsigned int analogReadOversampled(byte analogChannel)  { // оверсемплинг, возвращает 11 битное значение 0...2046
  unsigned long aSum = 0;   // the sum of all analog readings
  for(unsigned int i = 1024; i > 0; i--){
    aSum = aSum + analogRead(analogChannel);
  }
  return aSum >> 9;   // эквивалент делению на 512
}
 
//////////////////////////////////////////////////////

 

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

https://www.youtube.com/watch?v=upBW61p9FDE

Видео по коду выше.