Чтение 16-и аналоговых сигналов через мультиплексор

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

uint8_t kol = 16;   // количество лучей;
uint8_t iter = 30;  // Число раз считывания с аналогового порта
uint8_t alarm = 0;  // Флаг тревоги.
uint16_t reference[16] = {0}; // Массив данных по лучам
uint16_t average = 0; // Среднее значение считанного

// -------------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT); // Индикатор работы
  uint16_t average = 0; // Среднее значение данных по лучу
  // Калибровка данных по kol лучам
  for (uint8_t i = 0; i < kol; i++)// Цикл по лучам
  {
    // функциzя установки адреса луча !!! Здесь пропущена
    average = readRey(); // Чтение аналогового порта iter раз
    reference[i] = average; // Запись среднего в массив по лучу.
  }
  digitalWrite(13, ! digitalRead(13)); // Мигаем для контроля
}
// -------------------------------------------------------------------------------------
void loop() {
  // Основная работа после калибровки.
  for (uint8_t i = 0; i < kol; i++) // Цикл по лучам kol раз
  {
    // функциzя установки адреса луча !!! Здесь пропущена
    average = readRey(); // на функцию чтение аналогового порта iter раз
    alarm = reference[i] - average; // разница между калибровкой и считанным
    // Для отладки вывод сравнения из массива и считанного
    Serial.print(i); Serial.print(":"); Serial.print(reference[i]); Serial.print("-");
    Serial.print(average); Serial.print(" = "); Serial.println(alarm);
    // Функция обработки сигнала тревоги !!! Здесь нет
  }
  digitalWrite(13, ! digitalRead(13)); // Мигаем для контроля
  Serial.println(); // Для отладки
}// конец loop()
// ======================================================================================
uint16_t readRey() // Чтение и усреднение данных по лучу
// Используется глобальная переменная iter
// Возврат среднее значенияе
{
  uint16_t dat = 0;
  for (uint8_t i = 0; i < iter; i++)
  {
    delay(5); // для устранения переходных процессов
    dat = dat +  analogRead(0); //Чтение с 0 аналоговога порта
  }
  dat = dat / iter;
  return dat; // вернули среднее значение считанного
}

Идея отладки такова, читаем данные с А0 перед этим якобы устанавливаем адрес на мультиплексоре. Вычисляем среднее арифметическое и записываем в массив данных. После калибровки пнреходим на основную работу. Устанавливаем адрес, читаем и усредняем данные, сравниваем с данными в массиве и при разнице идём на обработку ошибки.

В чём проблема! При сравнении “‘эталона“ из массива и считанного сейчас большая разница. Причём это при закороченной А0 на землю. Ещё непонятка. Разница уменьшается от нулевого адреса к старшему.

Подскажите куда копать?

никакого “якобы установки адреса” в этом коде нет. Вы тупо читаете А0 30 раз по числу входов. И ничего больше.

добавьте в setup вывод значений, возвращаемых readRey() перед занесением их в массив reference и покажите вывод

Написал функцию проверки содержимого массива reference[16]. Вставил вызов функции в разделе setup() до цикла калибровки данных по лучам. Привожу результат:

image

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

А текстом не судьба была выложить?

Ну ОК, если по другому не умеете - смотрим.

Этот ряд чисел должен совпадать с первыми числами на каждой строке на этой выдаче:

А на деле - огромная разница.
Из этого следует, что ваш массив reference где-то между setup() и loop() портится. Поскольку в приведенном Вами коде ничего похожего нет, я делаю единственный возможный вывод - вы выложили на форум не тот код, который используете сами. Собственно, вы и сами об этом пишете:

Пожалуйста, покажите полный код. Ну или не показывайте - но тогда помощи не ждите.

Так же нет раздела обработки разницы показаний между “эталоном“ и прчитанным. Разобраться бы в том, что получается сейчас.

В эмуляторе всё корректно - Wokwi - Online ESP32, STM32, Arduino Simulator

Если вы умеете разбираться в одном коде по результатам работы совсем другого - разбирайтесь. Я не умею.

Да я и не сомневаюсь.

ТС выложил нам один код и просит по нему отыскать ошибки в другом коде :slight_smile: Приколист

@Vitalmsk
Вы понимаете, что ваши просьбы о помощи абсурдны без кода? Или вы настолько далеки от программирования, что это для вас не очевидно?

Привожу полный код, без вырезок.

uint8_t kol = 16; // количество лучей;
uint8_t iter = 30; // Количества итераций считывания с аналогового порта
uint8_t koef = 100; // Коэффициент расхождения дельта ошибки.
uint8_t alarm = 0; // Флаг тревоги.
uint16_t reference[16] = {0}; // Массив данных по лучам
uint8_t malarm[16] = {0}; // Массив данных тревог.
uint16_t average = 0; // Среднее значение

#include "CyberLib.h" 

// -------------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT); // Индикатор  работы
  DDRD = DDRD | B00111100; // инициализация пинов 2,3,4,5 на выход
  
  //unsigned int 
  uint16_t average = 0; // Среднее значение данных по лучу
  readReference ();// Для отладки проверка массива перед его заполнением.
     for (uint8_t i = 0; i < kol; i++)// Цикл по лучам
       {
          nambRey(i); // На функцию установки адреса луча
          for(uint8_t j = 0; j < 3; j++) 
          {
             average = readRey(); // Чтение аналогового порта iter раз
             reference[i] = average; // Запись среднего в массив по лучу.
          }
          average = 0;
       }
       digitalWrite(13, ! digitalRead(13)); // Мигаем для контроля
 // readReference (); // Контрольное чтение массива дпнных по лучам. Отладка

}
// -------------------------------------------------------------------------------------
void loop() {
// Основная работа после инициализации.
   for(uint8_t i = 0; i < kol; i++)// Цикл по лучам
   {
      nambRey(i); // На функцию установки адреса луча
      average = readRey(); // Чтение аналогового порта iter раз
      alarm = reference[i] - average; 
     // alarm = abs(alarm); // Получили сигнал тревоги
//     if (alarm > koef) {mAlarm(i);} // На обработку тревоги
      Serial.print(i); Serial.print(":"); Serial.print(reference[i]);Serial.print("-");Serial.print(average);Serial.print(" = "); Serial.println(alarm);
   }
   
   digitalWrite(13, ! digitalRead(13)); // Мигаем для контроля
   // delay(9000);
   Serial.println();
}//Loop()
// ======================================================================================
void nambRey(uint8_t n) // Расчет и установка луча опроса
  {
  /* Цикл для перебора адресов используя 2,3,4,5 ноги
   * Значения 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60 
   * На входе n номер луча
   */ 
   int k;
     n = n * 4;              // Расчет следующего адреса
     // Serial.print("лучь = ");Serial.print(n);Serial.print(" "); // Отладка
     PORTD &=  B11000011;  // Обнуляем рабочие пины
     PORTD |= n;           // Выставляем значение адреса на выход
  }
//------------------------------------------------------------------
 uint16_t readRey() // Чтение и усреднение данных по лучу
 // Используется глобальная переменная iter
 // Возврат среднего значения
   {
    uint16_t dat = 0;
    for(uint8_t i = 0; i < iter; i++) 
    {
      delay(10);
      dat = dat + A0_Read;
    }
    dat = dat / iter;
    return dat;
   }
 // ------------------------------------------------------------------  
void mAlarm(uint8_t n) // Массив сигналов тревоги
/*  Накопление сигналов тревоги по лучам
 *  на входе имеем номер тревожного луча 
 *  Если количество тревог > максимума по лучу,
 *  то данные количества тревог по лучу обнуляется
*/
{
  malarm[n] = malarm[n] + 1; // Изменили количество тревог по лучу
  
  for(uint8_t i = 0; i < kol; i++)
     { 
      if (malarm[i] > 5) {malarm[i] = 0;} // Сбросили максимум тревог 
     // Serial.print(i); Serial.print("-");Serial.println(malarm[i]);
      
     }
 
}
// =============  Для отладки. ==================
//-----------Чтение массива reference.-----------
void readReference ()
{
  Serial.println( "Массив");
  for (uint8_t i = 0; i < kol; i++)
   {
     Serial.print(i);
     Serial.print(" = ");
     Serial.print(reference[i]);
     Serial.println(", ");
   }
Serial.println();
}

Кстати, а вы пробовали запустить урезанный код? Интересно какие результаты вы получили?

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

Запустил ваш код из сообщения №12:

Массив
0 = 0, 
1 = 0, 
2 = 0, 
3 = 0, 
4 = 0, 
5 = 0, 
6 = 0, 
7 = 0, 
8 = 0, 
9 = 0, 
10 = 0, 
11 = 0, 
12 = 0, 
13 = 0, 
14 = 0, 
15 = 0, 

0:0-0 = 0
1:0-0 = 0
2:0-0 = 0
3:0-0 = 0
4:0-0 = 0
5:0-0 = 0
6:0-0 = 0
7:0-0 = 0
8:0-0 = 0
9:0-0 = 0
10:0-0 = 0
11:0-0 = 0
12:0-0 = 0
13:0-0 = 0
14:0-0 = 0
15:0-0 = 0

Плата Ардуино Нано, ИДЕ 1.8.19, Линукс

Вывод - Никаких проблем не наблюдаю.

а потом меняю одну вещь… и получаю ваш вывод:


0:294-175 = 119
1:237-174 = 63
2:205-174 = 31
3:189-174 = 15
4:182-174 = 8
5:178-173 = 5
6:177-173 = 4
7:177-173 = 4
8:176-173 = 3
9:176-173 = 3
10:176-174 = 2
11:176-174 = 2
12:176-174 = 2
13:177-174 = 3
14:177-175 = 2
15:176-175 = 1

Вопрос - что я поменял?

Ну и что поменяли? Секрет фирмы, тайна вклада?

Не поверите - просто убрал перемычку, замыкающую A0 на GND.
В принципе, глядя на ваш листинг из самого первого сообщения:

у меня сразу была такая мысль. Потому что самое простое и логичное обьяснение такому поведению - никуда не подключенный аналоговый вход, который ловит все окружающие наводки. Но я поверил Вам, когда вы написали, что притянули А0 к земле.

После запуска теста все стало очевидно.

Я не знаю, вводили ли вы нас в заблуждение или это получилось случайно… но аналоговый вход А0 в ваших тестах не заземлен. Может перепутали вход… или непропай на плате… или перемычка с пина соскочила…

В общем, ищите проблемы с железом. Код здесь не при чем.

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

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