Проблема с отображением истинного среднеквадратичного значения основного напряжения с использованием Arduino Uno и MAX7219

Я хочу отобразить истинное среднеквадратичное значение основного напряжения, используя Arduino и MAX7219. Сначала я использовал схему №1 (следующая схема) и библиотеку True RMS. Схема может успешно возвращать истинное среднеквадратичное значение и печатать его на последовательном мониторе Arduino каждые 0,5 с. Точность была приемлемой. Эта схема была запитана от ПК.

Контур №1

На следующем этапе я решил использовать MAX7219 (библиотека LedControl) для отображения истинного среднеквадратичного значения напряжения на 7-сегментном дисплее. Для этого я использовал схему №2. Я не знаю, что случилось. Отображаемое напряжение имело вариации. В схеме №1 максимальная погрешность составила 1 вольт (проверено мультиметром истинного среднеквадратичного значения - например, напряжение было 210 В, а напечатанное напряжение было 209 или 211 В). А вот в схеме №2 ошибка была до 10 В с быстрым изменением. Например, напряжение было около 210 В (с помощью мультиметра) в течение минуты, но отображаемое напряжение Arduino было 203, затем 209, затем 217, затем 205, затем 200, затем 215, ....

Я также использовал другой мультиметр с истинным среднеквадратичным значением для проверки первого мультиметра. Напряжение было таким же, как и первое. Проблема заключалась в том, что напряжение оставалось примерно постоянным, а Arduino показывал переменное напряжение. Проверил подаваемое напряжение с ПК. В нем нет эффективных изменений.

Я также использовал схему № 3, но у меня все еще была та же проблема. Похоже, проблема связана с использованием MAX7219. Каково решение?

Контур №2

Мой код (для двухцветного 7-сегментного общего анода - у меня 3 цифры):

#include <TrueRMS.h>
#include <digitalWriteFast.h> // Он использует digitalWriteFast только для целей отладки!
                              // https://code.google.com/archive/p/digitalwritefast/downloads

#define LPERIOD 1000    // 1000 циклов времени в нас. В этом случае 1,0 мс
#define ADC_INPUT 0     // определяем используемый входной канал АЦП
#define RMS_WINDOW 40   // окно среднеквадратичного значения из 40 отсчетов, что означает 2 периода при 50 Гц
// #define RMS_WINDOW 50 // окно среднеквадратичного значения из 50 отсчетов, что означает 3 периода при 60 Гц

#define PIN_DEBUG 4

unsigned long nextLoop;
int adcVal;
int cnt = 0;
float VoltRange = 5.00; // Значение полной шкалы установлено на 5,00 Вольт, но может быть изменено при использовании
                        // входная схема масштабирования перед АЦП.
Rms readRms; // создать экземпляр Rms.

// Max7219 ============================================== ================
#include "LedControl.h"
int ClockPin = 5;
int CsPin = 6;
int DinPin = 7;
LedControl lc = LedControl(DinPin, ClockPin, CsPin, 1);
// 1, так как мы используем только 1 MAX7219

void setup() {
  Serial.begin(115200);
  pinMode(PIN_DEBUG, OUTPUT);
  
  // настроить автоматическое восстановление базовой линии и режим непрерывного сканирования:
  readRms.begin(VoltRange, RMS_WINDOW, ADC_10BIT, BLR_ON, CNT_SCAN);
  //readRms.begin(VoltRange, RMS_WINDOW, ADC_10BIT, BLR_OFF, CNT_SCAN);
  // настроить без восстановления базовой линии и режим одиночного сканирования:
  //readRms.begin(VoltRange, RMS_WINDOW, ADC_10BIT, BLR_OFF, SGL_SCAN);
    readRms.start(); // начинаем измерение
    nextLoop = micros() + LPERIOD; // Установить переменную таймера цикла для следующего интервала цикла.

  // Макс7219
  // ноль относится к номеру MAX7219, это ноль для 1 чипа
    lc.shutdown(0, false); // отключить энергосбережение, включить отображение
    lc.setIntensity(0, 15); // 15 настроек яркости (от 0 до 15 возможных значений)
    lc.clearDisplay(0); // очисти экран
  }

// Макс7219
byte board[1][8] = {
  {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000
  }
};

// Max7219 Устройство
void ClearDisplay(int b, int digit) {
  for (int i=0; i<8; i++) { // Он очищает все сегменты Digit "digit" устройства "n"
    bitClear(board[b][i], 7-digit);
  }
}

void Display() {
  for (int i=0; i<9; i++){
     lc.setRow(0, i, board[0][i]);
  }
}

// Число цифр устройства точка
void SetNum(int b, int digit, int num, int dot) {
switch(num) {
  case -2: // отображает "-"
    bitClear(board[b][0], 7-digit); // сегмент А
    bitClear(board[b][1], 7-digit); // сегмент-B
    bitClear(board[b][2], 7-digit); // сегмент C
    bitClear(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case -1: // очищает дисплей
    bitClear(board[b][0], 7-digit); // сегмент А
    bitClear(board[b][1], 7-digit); // сегмент-B
    bitClear(board[b][2], 7-digit); // сегмент C
    bitClear(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitClear(board[b][6], 7-digit); // сегмент-G
    break;
  case 0:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitSet(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitClear(board[b][6], 7-digit); // сегмент-G
    break;
  case 1:
    bitClear(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitClear(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitClear(board[b][6], 7-digit); // сегмент-G
    break;
  case 2:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitClear(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitSet(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 3:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 4:
    bitClear(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitClear(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 5:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitClear(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 6:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitClear(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitSet(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 7:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitClear(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitClear(board[b][5], 7-digit); // сегмент-F
    bitClear(board[b][6], 7-digit); // сегмент-G
    break;
  case 8:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitSet(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
  case 9:
    bitSet(board[b][0], 7-digit); // сегмент А
    bitSet(board[b][1], 7-digit); // сегмент-B
    bitSet(board[b][2], 7-digit); // сегмент C
    bitSet(board[b][3], 7-digit); // сегмент-D
    bitClear(board[b][4], 7-digit); // сегмент-E
    bitSet(board[b][5], 7-digit); // сегмент-F
    bitSet(board[b][6], 7-digit); // сегмент-G
    break;
    default:
    break;
  }
  if (dot == 1) {
    bitSet(board[b][7], 7-digit); // seg-dp
  }
}

// 0-1-2-3
// Число цифр устройства Цвет точек
void SetNumWithColor(int b, int digit, int num,  int dot, char color) {
  switch(color) {
    case 'r': // цифры 0,1,2,3 красные
      SetNum(b, digit, num, dot);
      SetNum(b, 4, -1, dot); // очищает все зеленые сегменты
      SetNum(b, 5, -1, dot); // очищает все зеленые сегменты
      SetNum(b, 6, -1, dot); // очищает все зеленые сегменты
      SetNum(b, 7, -1, dot); // очищает все зеленые сегменты
      break;
    case 'g': // цифры 4,5,6,7 зеленые
      SetNum(b, digit+4, num, dot);
      SetNum(b, 0, -1, dot); // очищает все красные сегменты
      SetNum(b, 1, -1, dot); // очищает все красные сегменты
      SetNum(b, 2, -1, dot); // очищает все красные сегменты
      SetNum(b, 3, -1, dot); // очищает все красные сегменты
      break;
    case 'o':
      SetNum(b, digit, num, dot); // Включает красный
      SetNum(b, digit+4, num, dot); // Включает зеленый
      break;
    default:
      break;
  }
  Display();
}

void loop() {
  adcVal = analogRead(ADC_INPUT); // прочитать АЦП.
  // чтениеRms.update(adcVal); // Обновить
  digitalWriteFast(PIN_DEBUG, HIGH);
  readRms.update(adcVal); // для сигналов BLR_ON или DC(+AC) с BLR_OFF
  // readRms.update(adcVal-512); // без автоматического восстановления базовой линии (BLR_OFF); здесь вычтите фиксированное смещение постоянного тока в единицах АЦП.
  digitalWriteFast(PIN_DEBUG, LOW);                                

  cnt++;
  if (cnt >= 1500) { // 500 = публиковать каждые 0,5 с
    readRms.publish();
    float RMS=readRms.rmsVal;
    float AccRMS=RMS*128;
    int RMSout=AccRMS;
    Serial.print("Vrms=");
    Serial.print(RMSout);
    int a1=RMSout/100;
    int b1=RMSout%100;
    int a2=b1/10;
    int a3=b1%10;
    if (RMSout<200) { // Красный
      SetNumWithColor(0, 0, a1,  0, 'r'); // отображает a1 в цифре 0
      SetNumWithColor(0, 1, a2,  0, 'r'); // отображает a2 в цифре 1
      SetNumWithColor(0, 2, a3,  0, 'r'); // отображает a3 в цифре 2
    }else if(RMSout<215){ // Оранжевый
      SetNumWithColor(0, 0, a1,  0, 'o'); // отображает a1 в цифре 0
      SetNumWithColor(0, 1, a2,  0, 'o'); // отображает a2 в цифре 1
      SetNumWithColor(0, 2, a3,  0, 'o'); // отображает a3 в цифре 2
    }else{ // Зеленый
      SetNumWithColor(0, 0, a1,  0, 'g'); // отображает 1 в цифре 0
      SetNumWithColor(0, 1, a2,  0, 'g'); // отображает 2 в цифре 1
      SetNumWithColor(0, 2, a3,  0, 'g'); // отображает 3 в цифре 2
    }
    Serial.print(", Vin=");
    Serial.println(RMS,4);
    // float dc=readRms.dcBias;
    // Serial.println(dc,4);
    cnt=0;
    // чтениеRms.start(); // Перезапустить сбор данных после публикации, если выбран режим одиночного сканирования.
    
  }

  while (nextLoop > micros());  // ждем окончания временного интервала цикла
  nextLoop += LPERIOD;  // устанавливаем время следующего цикла на текущее время + LOOP_PERIOD

}

// конец Measure_rms.ino

, 👍2


1 ответ


0

Не видя вашего кода, трудно догадаться полностью, но это мое предположение:

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

Вы должны убедиться, что выборка сигнала переменного тока происходит достаточно быстро, не делая ничего другого (много) в течение периода выборки и отображая что-либо на дисплее только тогда, когда вся выборка для текущей точки данных выполнена.< /p>

,

Спасибо. Перед отображением рассчитывается среднеквадратичное значение за два периода. Итак, обновление происходит после измерения и расчета. Код находится здесь (https://github.com/MartinStokroos/TrueRMS). На самом деле, я построил только A0, используя «Последовательный плоттер» Arduino. Я мог видеть, что амплитуда явно быстро меняется в цепи № 2 !!!! пока мультиметр этого не подтвердил. Но для схемы №1 этого не произошло вообще. Почему? Влияет ли Max7219 на вход A0 или синусоиду сетевого напряжения?, @soheil

Это только библиотека и примеры. Это не *ваш* код., @Majenko

Я добавил свой код. Спасибо, @soheil

@soheil ответ применим и к setup (), @Juraj