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

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

Итак, открываю тему потому что берусь за новый проект, это будет моя дипломная робота. Идея состоит в чём? Зашить в ATmega 8 загрузчик arduino ну и дуиновскими функциями уже запрограммировать контролер под многоканальный вольтметр. Выводить информацию буду на дисплей 16 символов 2 строчки. Есть некоторые мысли сделать ещё отправку цифр в UART ну а потом на комп где программа будет рисовать графики всякие и т.д.

Итак, диапазоны для каждого канала вольтметра будут разные, планирую сделать под напряжения:

два до 13.5 В.

Два до  6 В.

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

Вот набросал код:

/*The circuit:
 * LCD RS pin to digital pin 2(PD2)
 * LCD Enable pin to digital pin 3(PD3)
 * LCD D4 pin to digital pin 4(PD4)
 * LCD D5 pin to digital pin 5(PD5)
 * LCD D6 pin to digital pin 6(PD6)
 * LCD D7 pin to digital pin 7(PD7)
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3)*/

#include <LiquidCrystal.h>
LiquidCrystal lcd(PD2, PD3, PD4, PD5, PD6, PD7);
unsigned long previousMillis = 0;        
byte interval = 250; // обновление дисплея         

void setup() { 
  Serial.begin(115200); // больше скорости, больше
  lcd.begin(16, 2);
  lcd.print("    *******   "); // факультет
  lcd.setCursor(0,1);
  lcd.print("  ********  "); // група
  delay(2000);
}

void loop() {
  //////////////////////////////////////////////////////
  unsigned int sensorValue0 = analogRead(0);
  unsigned int sensorValue1 = analogRead(1);
  unsigned int sensorValue2 = analogRead(2);
  unsigned int sensorValue3 = analogRead(3);
  unsigned int sensorValue4 = analogRead(4);
  unsigned int sensorValue5 = analogRead(5);
  //////////////////////////////////////////////////////
  float voltage0 = sensorValue0 * (13.50 / 1023.00);
  float voltage1 = sensorValue1 * (13.50 / 1023.00);
  float voltage2 = sensorValue2 * (6.00 / 1023.00);
  float voltage3 = sensorValue3 * (6.00 / 1023.00);
  float voltage4 = sensorValue4 * (265.0 / 1023.0); // для тестов
  float voltage5 = sensorValue5 / 4; // аналогично
  //////////////////////////////////////////////////////
  Serial.print(voltage0);
  Serial.print(",");
  Serial.print(voltage1);
  Serial.print(",");
  Serial.print(voltage2);
  Serial.print(",");
  Serial.print(voltage2);
  Serial.print(",");
  Serial.print(voltage3);
  Serial.print(",");
  Serial.print(voltage4);
  Serial.print(",");
  Serial.print(voltage5 * 6);
  Serial.println();
  //////////////////////////////////////////////////////
  unsigned long currentMillis = millis(); 
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis - 1;
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(voltage0);
    lcd.print(" ");
    lcd.print(voltage1);
    lcd.print(" ");
    lcd.print(voltage2);
    //lcd.print(" V");
    lcd.setCursor(0,1);
    lcd.print(voltage3);
    lcd.print(" ");
    lcd.print(voltage4);
    lcd.print(" V");
    lcd.print(" ");
    lcd.print(voltage5);
  }
}

Всё вроде как работает:

Но при программной симуляции МК постоянно перегружается, пробовал прошивать мегу 8-ю, то вроде как всё было норм:

http://www.youtube.com/watch?v=qqzBh1T4fI0

На видео генератор случайных цифр в определённом диапазоне, сварганить делитель ну и саму схему ещё не успел.

Вообщем приглашаю всех к обсуждению как моей схемы, задумки и кода.

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

Кстати, код занимает почти всю память "Размер скетча в двоичном коде: 6 048 байт (из 7 680 байт максимум)" Помогите с оптимизацией и ускорением отправки даных в UART для построения более точных графиков, очень хочется.

ЗЫ на схеме выше нету кварцевого резонатора на 16 мГц, в схеме он обязательно будет, ядро будет используватся Optiboot.

 
Илья73
Offline
Зарегистрирован: 06.09.2013

.

Сам в ближайшем будущем планирую что то подобное сделать с питанием от батареи LiPo в одну банку. Но только хочу мерять и ток и напряжение и ватты, и потребленные ваты, с записью в ЕЕПРОМ. В общем если питать от батареи нужно наверно делать стабильный источник питания.

Клапауций
Offline
Зарегистрирован: 10.02.2013

HWman пишет:

Зашить в ATmega 8 загрузчик arduino...

Кстати, код занимает почти всю память "Размер скетча в двоичном коде: 6 048 байт (из 7 680 байт максимум)" 

не шейте загрузчик, если места и так мало - будет maximum_size=8192

HWman пишет:

ядро будет используватся Optiboot.

чем обусловлена замена дефолтного ядра ардуино на ядро оптибут??

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

Я думал что без загрузчика дуиновский код мега не примет.

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

Да и вообще выбрал путь дуиновских функции потому что тут всё проще и понятней, по крайней мере мне.

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

Илья73 пишет:

.

Сам в ближайшем будущем планирую что то подобное сделать с питанием от батареи LiPo в одну банку. Но только хочу мерять и ток и напряжение и ватты, и потребленные ваты, с записью в ЕЕПРОМ. В общем если питать от батареи нужно наверно делать стабильный источник питания.

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

Да и моя цель - тестер для блоков питания ПК, есть ещё мысли сделать нагрузку на транзисторах для полноценного тестирования БП на исправность. Хотелось бы видеть пульсации напряжения в нагрузке чтобы находить больную линию в БП.

 

Клапауций
Offline
Зарегистрирован: 10.02.2013

HWman пишет:

Я думал что без загрузчика дуиновский код мега не примет.

не примет по юарту - программатором по ISP нормально прошьётся.

HWman пишет:

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

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

MaksMS
Offline
Зарегистрирован: 11.03.2013

Судя по строке : "Размер скетча в двоичном коде: 6 048 байт (из 7 680 байт максимум)" 

размер загрузчика 512 байт - это и так минимум

А на счет оптимизации - надо аналог читать напрямую без адруиновских функций

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

забей на бутлоадер. из иде можно напрямую программатором писать, и это удобнее, если конечно UART не использовать

поставь маленький конденсатор по каждому входу чтобы цифры не бегали сильно

может для диплома поставишь получше дисплей? или это устраивает? 

ну хоть 20*4. подпишешь что должно быть через двоеточие измеренное. так было бы лучше мне кажется

http://www.ebay.com/itm/New-2004-204-20X4-Character-LCD-Module-Display-For-Arduino-/350560713502?pt=LH_DefaultDomain_0&hash=item519f0bfb1e

http://www.ebay.com/itm/1-8-Serial-128X160-SPI-TFT-LCD-Modul-Display-PCB-Adapter-SD-Socket-4-Arduino-/280937300096?pt=UK_BOI_Electrical_Components_Supplies_ET&hash=item41692b0080

для второго незнаю требуется большой видеобуфер или нет

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

7 баксов? Дороговато, да и 16х2 у меня уже есть, а этот покупать нужно.

И без того будет на что тратить денежку, схему нужно распечатать на листе А1 ну и ещё чего-то там на 3 таких же листа, фото монтажной платы, блок-схема и ещё что-то.

Да и зачем 20х4 если всё помещается на 16х2:

V1=13.50 V2=6.00 

V3=13.50 V4=6.00 

Ровно 16 символов, то что мне надо.

А насчёт прошивки по UARTу -  а почему бы и нет? Тем более, шью я дуинкой, ещё есть программатор Громова, но я его сделал почти одноразовым, весь разваливается.

 

Клапауций
Offline
Зарегистрирован: 10.02.2013

HWman пишет:

А насчёт прошивки по UARTу -  а почему бы и нет? Тем более, шью я дуинкой, ещё есть программатор Громова, но я его сделал почти одноразовым, весь разваливается.

по SPI...

*шото смущают меня такие будущие спенциалисты - потом ракеты падают.

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

Я не волшебник, я только учусь, рано мне ещё ракеты сбивать.

Кстати, не всё так плохо, есть ещё спецы покруче.

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

Итак, набросал примерно части вольтметра:

 

Жду критики...

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

У меня появилась идея, чтобы нормально защитить свою схемку "от дурака" резшыл поставить вместо диодов диодные мосты:

При таком раскладе юзер не сможет убить МК переполюсовкой, никак + можно будет мерить даже переменку, но для неё нужно по электролиту на каждый АЦП.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

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

мне кажется лучше мост убрать

но если измерять например в диапазоне 3-6В и 9-14В (не от нуля) то теоретически можно и так. но надо проверять

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

Ну так подстроечным резистором откалибрую чтобы всё правильно показывало.

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

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

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, мост на входе вольтметра это изврат.  Стабилитрон на 5 вольт на входе АЦП ставят только когда analogReference гораздо меньше чем 5 вольт. Подстроечный резистор не должен регулировать в таких широких пределах. И вообще нет смысла что-то подстраивать для единичного образца. Рассчитать делитель, и при отладке  измерить точно полученный  коэффициент, и внести его в скетч. Аналоговый вход питания avcc желательно через индуктивность, и емкость на общий. А на на ноге Aref желательно конденсатор 10н..100н.

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

Спасибо, учту.

Итак, с мостами ничего не получилось, нелинейно там всё, скорей всего буду делать как-то так только стабилитрон будет на 5.6 В чтобы почти не мешал на всём диапазоне АЦП, тест на переполюсовку и завышение напряжения дуинка выдержала.

Вот другая проблема, почему то цыфры бегают:

На входе 12 В аккум... Может кто что подскажет?

На дуинке мега8-я, вольтметр собран по такой схеме

Только ещё по + резистор на 2 к, ато если подстроечник выкрутить вверх то запросто можно схечь стабылитрон, и он превращается в КЗ.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, Для начала отказаться от подстроечнного резистора, от него вреда больше чем пользы. Поставьте 10 ком от ноги ацп на общий, и как можно ближе к выводам контроллера, это важно. Второй резистор рассчитать под нужное максимальное входное напряжение стандартной формулой делителя напряжения.  От дефолтового референсного 5 вольт лучше отказаться. Как минимум использовать внутренний ИОН, а ещё лучше внешний, а какой нибудь REF195/198 вообще шикарно. Если ничего не поможет, тогда можно пробывать усреднять программно. Но лучше выжать масимально аппаратно прежде чем усреднять результаты. У меня от внутреннего reference  в меге168 девиация показаний analogRead не более +/- 1 единицы счёта. Не думаю, что в меге 8 хуже. Судя по вашим цифрам у вас (13,04-12,93)*1023/5=22      Т.е.   +/-   11 единиц счёта, что явно много.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

ref198 вообще шикарно))

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

Я на днях юзал lm317 и очень понравился уровень стабилизации напряжение, на вход подавал 5-25 В на выходе стабильно было 1.500 +/- 1 мВ, вот и думаю а может сделать как-то на 317-ке? тем более у меня их есть 2 штуки. Вот только думаю делать только для одной ножки, или же сделать всё питание? Сделать по входу и выходу норм фильтры(кроме керамики ещё конденсаторы на 1000 мкФ). Всё это дело будет питаться от трансформаторного БП на 10-12 В.

Вот набросал схему на 5 В:

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

Мда, а вот с шумом АЦП я такого не ожидал, думал что будет как у dimax"а +/- 1 значение АЦП.

PS С каждой новой железкой мне нужно будет искать и переводить на неё документацию чтобы что-то по ней было в работе. Хочется сделать как бы максимально просто.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, у лм 317 может быть шум на выходе, который вы подручными тестерами не увидите, разве что очень хорошим осциллографом. Этот шум будет только усиливать пляску показаний вольтметра. Лучше используёте внутренний ион меги, или ставьте что-то приличное.

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

Собрал делитель по схеме и...

Похоже на то что я использовал слишком большой номинал в резисторе делителя, аж 100 кОм.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, неплохо бы комментировать что вы измеряете. Как я понял, на входе снова 12 вольт с аккумулятора, а вертикальная шкала в "попугаях" команды analogRead ? Если так, то девиация соответствует норме. Можете остальное добить программно.

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

Да, вертикальная шкала это значение которое возвращает АЦП, по питанию дуинки стоит ещё конденсатор на 3300 мкФ, без него больше "скачет".

Точность если уж грубо получилась по линиях 0-15 +/- 0.05 что есть вполне терпимо для моей задачи, А можно как-то спрятать те цифры что идут после десятых? Гуглил на тему округление ардуино и толком ничего не нашёл, может кто подскажет что? Мне нужно на ЛСД выводить например 12.8 а выводит 12.83, причём нужно не округлять а игнорировать цифры после десятых, вот такой у меня каприз.

На ум приходит умножить число на 10 и преобразовать его в инт потом обратно, но, дуинка почему-то выводит не 12.8 а 12.80 если использовать тип флоат...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, с ёмкостью правильно, а источник опорного напряжения в итоге так и остался дефолтовый?

Спрятать лишние цифры легко: lcd.print(voltage0,1); Цифра после запятой -количество цифр выводимых после запятой.

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

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

/*Спрятать лишние цифры легко: lcd.print(voltage0,1); Цифра после запятой -количество цифр выводимых после запятой.*/

Спасибо.

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

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

Как-то больше шума при внутренем ИОНе.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman пишет:

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

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

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

}

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

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

Спасибо, только не совсем понял что делает этот код, типо плавное увеличение и плавное уменьшение value ?

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, эта команда просто не пропускает нестабильные данные на единицу отличающиеся от прочтённых ранее. Вариант программного устранения шума ацп вместо традиционного усреднения.  Ваш график нарисовал бы ровную линию.

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

А можно и порог "шумодава" задавать, если слегка модифицировать код:

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

в данном случае значение переменной threshold и есть порог срабатывания. здесь он равен величине 2.

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

Спасибо за код.

Так как я выведу ноги RXD TXD RES GND то в принципе можно будет шить через этот разъём правда? Ардуиновский загрузчик уже в меге зашит ещё со времён как я в уно поджёг 328р, перебивался 8-й мегой, тогда и зашил загрузчик.

Вот только не могу найти информации по прошивке дуинки по юарту, может подскажет кто?

Клапауций
Offline
Зарегистрирован: 10.02.2013

HWman пишет:

Вот только не могу найти информации по прошивке дуинки по юарту, может подскажет кто?

так, а чего искать-то - цепляешь к RX-TX дуино TX-RX железки сом-порта и шьёшь как и всякую дуино.

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

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

/*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 pin to ground
 * 1K resistor:
 * ends to +5V and ground
 */

#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
unsigned long previousMillis = 0;        
unsigned int interval = 330; // обновление дисплея         
unsigned int  sensorValue2, sensorValue3, sensorValue4, sensorValue5;

void setup() { 
  Serial.begin(115200); // больше скорости, больше
  lcd.begin(16, 2);
  lcd.print("Multi-channel"); // ...
  lcd.setCursor(0,1);
  lcd.print("Voltmeter v 0.7t"); // ...
  delay(3000);
}

void loop() {
  //////////////////////////////////////////////////////
  sensorValue2 = 0.0;
  sensorValue3 = 0.0;
  sensorValue4 = 0.0;
  sensorValue5 = 0.0;
  for(byte c = 0; c < 10; c++){ // 10 раз измерь...
    sensorValue2 = sensorValue2 + analogRead(2);
    sensorValue3 = sensorValue3 + analogRead(3);
    sensorValue4 = sensorValue4 + analogRead(4);
    sensorValue5 = sensorValue5 + analogRead(5);
  }

  if(sensorValue2 >sensorValue2+25 || sensorValue2<sensorValue2-25){ // подавление шума АЦП
    sensorValue2=sensorValue2 ;
  }
  if(sensorValue3 >sensorValue3+25 || sensorValue3<sensorValue3-25){
    sensorValue3=sensorValue3 ;
  }
  if(sensorValue4 >sensorValue4+25 || sensorValue4<sensorValue4-25){
    sensorValue4=sensorValue4 ;
  }
  if(sensorValue5 >sensorValue5+25 || sensorValue5<sensorValue5-25){
    sensorValue5=sensorValue5 ;
  }

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

  float voltage2 = (sensorValue2 * (15.00 / 1023.00))/10; // получаем среднее арифметическое
  float voltage3 = (sensorValue3 * (15.00 / 1023.00))/10;
  float voltage4 = (sensorValue4 * (6.50 / 1023.00))/10;
  float voltage5 = (sensorValue5 * (6.50 / 1023.00))/10;

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

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

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

  unsigned long currentMillis = millis(); 
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;
    //lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("V1=");
    if(voltage2 > 14.99) {
      lcd.print("MAX");
    }
    else{
      lcd.print(voltage2);
    }
    if(voltage2 < 9.99) { // пробел чтобы не напр. не уходило в сторону
      lcd.print(" ");
    }
    lcd.print("V2="); 
    if(voltage3 > 14.99) {
      lcd.print("MAX");
    }
    else{
      lcd.print(voltage3);
    }
    lcd.print(" ");
    lcd.setCursor(0,1);

    lcd.print("V3=");
    if(voltage4 > 6.49) {
      lcd.print("MAX ");
    }
    else{
      lcd.print(voltage4);
    }
    lcd.print(" V4=");
    if(voltage5 > 6.49) {
      lcd.print("MAX ");
    }
    else{
      lcd.print(voltage5);
    }
    lcd.print(" ");
  }
}




Схему ещё не нарисовал, LC фильтр по питанию, везде конденсаторы на 0.1 мкФ, и по питанию меги и на АЦП, делитель собрал как тут, только делитель рассчитал на 15 В макс.(10к и 20 к ) пока без диодов, стабилитрон начинает влиять на измерения где-то на 95% значения АЦП, в целом это вполне терпимо.

Иногда Непонятно от чего LCD дисплей начинает выводить всякие кракозябры хотя в юарт идут нормальные цифры, подозреваю что это из за того что я для удлинения использую IDE шлейф от ПК, хотя может что-то и с самим дисплеем. 
Позже может видео сниму.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, ну и наворотили вы дел...., всю секцию от начала loop и до вывода на экран нужно переделывать. Там полный хаос...

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

Почему же? Сначала обнулим переменные, потом 10 раз делаем измерение, подавляем шумы, получаем среднее арифметическое, выводим цыфри в сериал. 
Я переделаю с удовольствием но что?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, тот метод "шумодава", о котором я писал выдаёт уже полностью усреднённый результат,  и он не совместим с методом высчитывания среднего-арифметического. Получается сначала вы восстанавливаете  максимальную точность, потом всё полученное снова срезаете, в чём смысл? Нужно выбрать либо одно либо другое.

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

Просто с подавлением шума меньше дёргаются сотые на линии 0-15В, надо будет ещё поэкспериментировать...
Вот так на данный момент выглядит плата:

Как видите стабилитроны только на 2-х каналах пока, на одному 0-15В и второй на 0-6В, хочу увидеть на сколько они влияют на измерения. Вот график разряда конденсатора который удалось получить при помощи моего вольтметра:

Синий - с стабилитроном, зелёный просто делитель, как видим стабилитрон начинает влиять уже после 6.2 В, но я делаю 2 канала на 0-6В так что всё что выше 6В будет игнорироваться и выводить что на максимальное напряжение на канале.

Земля делителей соединяется с землёй контролера только в одной точке(чистая земля), все минуса LCD подключении к земле делителя(грязная земля).

Не знаю правильно я сделал что разместил дроссель LC фильтра питания около кварца.
Видео работы 
http://www.youtube.com/watch?v=ybsw6OIVI74

А вот глюки при выводе напряжения на LCD http://www.youtube.com/watch?v=Oy2KahrZnE0
Иногда появляются когда землю прикоснуться до  "-" аккума, иногда сами по себе появляются, если коснуться почти любого контакта LCD то иногда глюки пропадают и начинает нормально выводить цифры, как я говорил, в юарт цифры идут нормальные не зависимо кракозябры ли на LCD или нет.

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, в плане избавления от мандража показаний метод среднего арифметического малоэффективен. Либо используйте то, что я предложил, либо ищите более эффективный способ. Для статических состояний нужна примерно такая методика - брать порядка 50  замеров, и определить математически, какой из результатов отсчётов был самый частый,  и этот результат считать правильным. По поводу lcd не совсем ясно, для начала керамику ему по питанию, я бы ещё качество земли проверил. После lcd.begin и перед первым обращением на вывод не помешает небольшую паузу вставить.

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

http://www.youtube.com/watch?v=Sz7y-EXY3nY

Если минусовой провод "чиркать" по минусу аккума то появляются/пропадают кракозябры, а если держать тот же провод пальцом и чиркать тогда всё нормально. Наверное я зря землю LCD развёл на грязную землю, я думал что это не на столько критично...

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

HWman, шлейф тоже нужно ликвидировать, сантиметров 15 оставить максимум. Ещё лучше без разъёмов, всё на пайку.  И керамику по питанию  само собой.

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

А делаю 10 раз измерение АЦП потому что если разово мерять то шумы достигают +/- 8-10, если же 10 раз измерять то +/- 2-3 с подавлением шума вообще +/- 1-2.

Ну, пока ещё проект не закончил, роботы ещё много, хочу если не весь код так большую его часть перевести на Си, ато итак "Размер скетча в двоичном коде: 6 276 байт (из 7 680 байт максимум)". 

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

Ещё вынашиваю идею сделать перевод порта в Z состояние когда АЦП будет возвращать 1023, такая себе программная защита от перенапряжения...

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

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

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

Дисплей на чистую землю не разводил по банальной причине - кончился припой.

 

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

Захотелось уменьшить энергопотребление отключая подсветку дисплея и переходом на 2 сек в сон если напряжение меньше 0.1 В на портах, но тут у меня возникла проблема, почему-то не работает прерывание по таймеру, оно как бы работает и как бы нет, дело в том что МК переходит в ресет после пробуждения а не продолжается выполнение кода где было вызвано прерывание по таймеру.
Вот код:

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

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;

// Обработчик прерываний
ISR (WDT_vect) 
{
  WDTCR |=_BV(WDE);   // разрешаем прерывания по ватчдогу, иначе будет резет!
}

void setup() { 

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

  pinMode(13, OUTPUT); // подсветка LCD 
  digitalWrite(13, HIGH); // привет
  Serial.begin(115200); // больше скорости, больше
  lcd.begin(16, 2);

}

void loop() {

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

  unsigned int sensorValue1 = 0;
  unsigned int sensorValue2 = 0;
  unsigned int sensorValue3 = 0;
  unsigned int sensorValue4 = 0;

  for(byte cyclesADC = 0; cyclesADC < 20; cyclesADC++){ // 10 раз измерь...
    sensorValue1 = sensorValue1 + analogRead(5);
    sensorValue2 = sensorValue2 + analogRead(4);
    sensorValue3 = sensorValue3 + analogRead(3);
    sensorValue4 = sensorValue4 + analogRead(2);
  }

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

  if(sensorValue1 >sensorValue1+45 || sensorValue1<sensorValue1-45){ // подавление шума АЦП
    sensorValue1=sensorValue1 ;
  }
  if(sensorValue2 >sensorValue2+45 || sensorValue2<sensorValue2-45){
    sensorValue2=sensorValue2 ;
  }
  if(sensorValue3 >sensorValue3+45 || sensorValue3<sensorValue3-45){
    sensorValue3=sensorValue3 ;
  }
  if(sensorValue4 >sensorValue4+45 || sensorValue4<sensorValue4-45){
    sensorValue4=sensorValue4 ;
  }

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

  float voltage1 = (sensorValue1 * (10.00 / 1023.00))/20; // получаем среднее арифметическое
  float voltage2 = (sensorValue2 * (10.00 / 1023.00))/20;
  float voltage3 = (sensorValue3 * (20.00 / 1023.00))/20;
  float voltage4 = (sensorValue4 * (20.00 / 1023.00))/20;

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

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

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

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

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

    unsigned long currentMillis = millis(); 
    if(currentMillis - previousMillis > interval) {
      previousMillis = currentMillis;
      //lcd.clear(); // бесит, можно обойтись и без
      lcd.setCursor(0,0);
      lcd.print("V1=");
      if(voltage1 > 9.99) {
        lcd.print("MAX ");
      }
      else{
        lcd.print(voltage1);
      }

      lcd.print(" V3=");
      if(voltage3 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage3);
      }
      lcd.print(" "); // нужно для того чтобы обходится без lcd.clear()
      lcd.setCursor(0,1);

      lcd.print("V2="); 
      if(voltage2 > 9.99) {
        lcd.print("MAX ");
      }
      else{
        lcd.print(voltage2);
      }

      lcd.print(" V4=");
      if(voltage4 > 19.99) {
        lcd.print("MAX  ");
      }
      else{
        lcd.print(voltage4);
      }
      lcd.print(" ");

    }
  }
  else{
    Serial.println("Sleep");
    delay(10); // для того чтобы адекватно отправлять Sleep
    digitalWrite(13, LOW);
    system_sleep_2S();
  }
}
//////////////////////////////////////////////////////

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


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

 

Может кто подскажет что у меня не так в коде?

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

Во всех апноутах по улучшению качества АЦП рекомендуют применять оверсемплинг -- суммирование большого числа измерений, с последующим вычислением среднего. Вычисление среднего может использовать небольшую хитрость, когда деление заменяется операциями сдвига. Сдвиг на один разряд вправо соответствует целочисленному делению на 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 битам дает и приемлемое усреднение и отчетливое увеличение точности.

 

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

Оу, спасибо, неплохо.

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

Черт, только что заметил, что шестая строка с опечаткой, а отредактировать форум не дает. Правильно она должна выглядеть так: return aSum >> 4;  Знака равенства там не должно быть.

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

Что "return aSum >>=4;" что "return aSum >> 4;" возвращает одно и тоже. Код работает чётко, удалось повысить точность до десятых вольта, использую во такой код:

 

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

Функция возвращает от 0 до 16368 что соответствует 14-ти битному АЦП, МК считывает 4 канала АЦП таким образом около 30 раз на секунду против 100 как раньше, но шумы поменьше, вообщем не велика потеря. При таком расширении можно спокойно делать делитель на 25 В, получится вольтметр на 20В, остальные 5В уже будет впрягаться стабилитрон, сделаю это мёртвой зоной, он как мне кажется будет и защищать от переполюсовки шунтируя АЦП при не правильном подключении.

В целом очень доволен такой вещью как оверсемплинг АЦП и скетч в целом не сильно вырос :) .

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