• I






      
           

Научно-популярный образовательный ресурс для юных и начинающих радиолюбителей - Popular science educational resource for young and novice hams

Основы электричества, учебные материалы и опыт профессионалов - Basics of electricity, educational materials and professional experience

КОНКУРС
language
 
Поиск junradio

Радиодетали
ОК
Сервисы

Stock Images
Покупка - продажа
Фото и изображений


 
Выгодный обмен
электронных валют

Друзья JR



JUNIOR RADIO

 

Виртуальный осциллограф на Arduino

 

Превратите свой Arduino в виртуальный осциллограф с частотой дискретизации 4,8 кГц

В этом проекте для захвата и визуализации формы сигнала используется встроенный АЦП. Он поддерживает различные режимы запуска (включая внешний запуск), позволяет пользователю выбирать аналоговый входной канал, опорное напряжение и управлять контактами микроконтроллера. Приложение для ПК, используемое в этом проекте, основано на платформе с открытым исходным кодом Simple Device Model (раскрытие: это я написал). Он поддерживает как Windows, так и Linux. Проект был протестирован на плате Arduino Uno и может работать или не работать на других Arduinos на базе AVR.

Отметим, что при частоте дискретизации 4808 Гц этот проект нельзя рассматривать как замену настоящему осциллографу.

Начиная

1. Подключите Arduino к компьютеру с помощью USB-кабеля и загрузите этот эскиз с помощью Arduino IDE. Обратите внимание на имя последовательного порта, используемого платой (например, COM3 в Windows или / dev / ttyACM0 в Linux).

2. Загрузите и установите платформу Simple Device Model.

3. Запустите sdmconsole , нажмите кнопку « Открыть плагин» и выберите демонстрацию UART в качестве плагина и Arduino Uno в качестве устройства.

4. В верхнем левом углу окна sdmconsole щелкните правой кнопкой мыши элемент Arduino Uno и выберите « Подключиться» . Введите имя последовательного порта, указанное выше.

5. Осциллограф теперь должен работать на аналоговом канале A0 . Если к нему ничего не подключено, вы, вероятно, увидите сетевые помехи 50 Гц (или 60 Гц, в зависимости от того, где вы живете).

6. Подключите цифровой контакт 3 к аналоговому входу A0 . В верхнем правом области, установлен контактный 3 режим с ШИМ и Pin3 PWM значения для 100 . Дважды щелкните область просмотра, чтобы сбросить масштаб. Теперь вы должны увидеть прямоугольную форму волны ШИМ:

7. Если вы хотите получить более плавный сигнал, поместите RC-цепь между выходом и входом. Для этого подойдут резистор ~ 100 кОм и керамический конденсатор ~ 1 мкФ.

Основные характеристики

В правой верхней панели настроек можно выбрать входной канал АЦП и опорное напряжение. Встроенный датчик температуры также можно настроить как входной канал. Учтите, что этот датчик температуры не очень точен.

Размер пакета устанавливает количество выборок в отображаемой форме волны. Это также влияет на частоту обновления. Для периодических сигналов установка размера пакета в соответствии с целым числом периодов обеспечит лучший обзор даже без синхронизации.

Раздел настроек контактов позволяет вам установить состояние контактов 2-13 MCU. Возможные состояния ввод , ввод с подтяжкой , Force низким и Force максимумом . Контакты, которые поддерживают ШИМ (широтно-импульсную модуляцию), также могут быть установлены в этот режим.

Режимы визуализации

В дополнение к базовому режиму «осциллограф» sdmconsole может визуализировать данные в виде гистограммы, изображения (где яркость пикселей представляет собой примерное значение) или двоичной диаграммы (где яркость пикселей представляет собой один бит). Режим визуализации можно изменить в раскрывающемся меню « Режим» .

Вы можете перемещаться по области просмотра, перетаскивая ее, изменять вертикальный масштаб с помощью Ctrl + колесо мыши и горизонтальный масштаб с помощью Ctrl + Shift + колесо мыши. Двойной щелчок в области видового экрана сбрасывает масштаб так, чтобы он соответствовал всему изображению.

Срабатывание

Режим синхронизации устанавливает режим триггера: « Выкл.» , « Нарастающий фронт» или « Спадающий фронт» . По умолчанию осциллограф запускается тем же сигналом, который выбран в качестве аналогового входа. Его также можно запустить с помощью цифрового вывода, выбрав его в раскрывающемся списке Источник синхронизации .

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

Смещение синхронизации устанавливает количество отображаемых выборок перед триггером.

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

Под капотом

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

Осциллограф управляется с помощью его виртуального адресного пространства: запись значения в назначенный адрес регистра интерпретируется как команда или данные конфигурации. Например, запись 3 в адрес регистра 0 выбирает канал аналогового ввода A3.

Конфигурация последовательного порта: 115200 бод, 8 битов данных, 1 стоповый бит, без контроля четности, без управления потоком.

В протоколе связи используются четыре типа фреймов:

Регистр записи (ПК -> Arduino):

01010000 ADDR[7:0] DATA[7:0]

Чтение реестра (ПК -> Arduino):

01010001 ADDR[7:0]

Регистрационные данные (Arduino -> PC):

1000 DATA[7:4] 0000 DATA[3:0]

Потоковые данные (Arduino -> ПК):

11 SOP DATA[9:5] 000 DATA[4:0]

(SOP - это флаг "начала кадра").

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

На стороне ПК протокол реализуется плагином uartdemo, который позволяет sdmconsole обмениваться данными с осциллографом. Его исходные коды поставляются с SDM (в каталоге примеров ). Плагин написан на C ++ и реализует функции для записи и чтения значений регистров и чтения потоковых данных с осциллографа.

Самый простой способ расширить набор функций осциллографа - это изменить его виртуальное адресное пространство (то есть функции writeVirtualRegister и readVirtualRegister ). Затем новые регистры могут быть добавлены в карту регистров, которая полностью доступна для редактирования пользователем (но не забудьте сохранить ее). Чтобы изменить протокол связи, вам также потребуется изменить и перекомпилировать исходники плагина uartdemo .

КОД

/*

 * ADC sampling decimation factor. Only one of each DECIMATION_FACTOR

 * samples will be written to the buffer. Increase this value if you

 * are having problems with communication reliability.

 *

 * The sampling frequency (in Hz) will be:

 *

 *   Fs = 16000000 (CPU clock frequency) / 128 (ADC prescaler) /

 *        13 (cycles per conversion) / DECIMATION_FACTOR

 */

#define DECIMATION_FACTOR 2

/*

 * A circular buffer to store up to 256 ADC samples. Indexes are of

 * "byte" type to make buffer overrun impossible.

 */

volatile unsigned int adcBuffer[256];

volatile byte adcWriteIndex=0;

byte adcReadIndex=0;

volatile bool triggered=false;

/* Digital pin states */

byte pinState[14];

byte pinPWM[14];

/* Synchronization settings */

volatile byte syncMode=0; /* 0 - off, 1 - rising edge, 2 - falling edge */

volatile byte syncSource=0; /* 0 - analog input, >0 - digital pin */

volatile unsigned int syncLevel=512; /* signal level for the oscilloscope to trigger */

byte syncOffset=128; /* how many samples before the trigger event will be displayed */

/* Packet size setting */

unsigned int packetSize=512;

void setup() {

/*

 * Configure the ADC

 * ADMUX:  REFS=01 (use AVCC for reference), MUX=0000 (use ADC0 input)

 * ADCSRB: ADTS=000 (free running mode, the new conversion will be started

 *         immediately after the previous one has been completed)

 * ADCSRA: ADEN=1 (enable ADC), ADSC=1 (start conversion),

 *         ADATE=1 (enable auto trigger), ADIE=1 (enable ADC interrupt),

 *         ADPS=111 (set ADC prescaler to 128)

 */

  ADMUX=0x40;

  ADCSRB=0;

  ADCSRA=0xEF;

  Serial.begin(115200);

}

void loop() {

/* Buffer for incoming data, can hold up to 3 bytes */

  static byte cmdBuffer[3];

  static byte cmdBytes=0;

  if(Serial.available()) {

/* Process incoming data */

    byte ch=Serial.read();

    if(cmdBytes==0) {

/* Look for a start of a packet */

      if(ch==0x50||ch==0x51) {

        cmdBuffer[0]=ch;

        cmdBytes++;

      }

    }

    else {

/* Packet continuation */

      cmdBuffer[cmdBytes]=ch;

      cmdBytes++;

    }

    if(cmdBuffer[0]==0x50&&cmdBytes==3) {

/* Write register */

      writeVirtualRegister(cmdBuffer[1],cmdBuffer[2]);

      cmdBytes=0;

    }

    else if(cmdBuffer[0]==0x51&&cmdBytes==2) {

/* Read register */

      byte data=readVirtualRegister(cmdBuffer[1]);

      byte buf[2];

      buf[0]=0x80|(data>>4); /* upper 4 bits */

      buf[1]=data&0x0F; /* lower 4 bits */

      Serial.write(buf,2);

      cmdBytes=0;

    }

  }

  else if(adcWriteIndex!=adcReadIndex) {

/* No incoming data, transmit data from the ADC circular buffer if present */

    static unsigned int sampleCnt=0;

    byte queue=adcWriteIndex-adcReadIndex;

    if(sampleCnt==0&&syncMode!=0) { /* start of packet, wait for trigger */

      if(queue<syncOffset) {

        triggered=false;

        return;

      }

      if(!triggered) {

        if(queue>syncOffset) adcReadIndex++;

        return;

      }

    }

    unsigned int data=adcBuffer[adcReadIndex++];

    byte buf[2];

    buf[0]=0xC0|(data>>5); /* upper 5 bits */

    if(sampleCnt==0) buf[0]|=0x20; /* start of packet mark */

    buf[1]=data&0x1F; /* lower 5 bits */

    Serial.write(buf,2);

    if(++sampleCnt>=packetSize) sampleCnt=0;

    triggered=false;

  }

void writeVirtualRegister(byte addr,byte data) {

/* ADC input channel */

  if(addr==0) ADMUX=(ADMUX&0xF0)|(data&0x0F);

/* ADC reference voltage */

  else if(addr==1) ADMUX=(ADMUX&0x3F)|(data<<6);

/* Pin mode registers */

  else if(addr>=2&&addr<=13) {

    pinState[addr]=data;

    setPinState(addr);

  }

/* PWM value registers */

  else if(addr>=18&&addr<=29) {

    pinPWM[addr-16]=data;

    setPinState(addr-16);

  }

/* Synchronization settings and packet size */

  else if(addr==32) syncMode=data;

  else if(addr==33) syncSource=data;

  else if(addr==34) syncLevel=static_cast<unsigned int>(data)<<2;

  else if(addr==35) syncOffset=data;

  else if(addr==36) reinterpret_cast<byte*>(&packetSize)[0]=data;

  else if(addr==37) reinterpret_cast<byte*>(&packetSize)[1]=data;

}

byte readVirtualRegister(byte addr) {

  if(addr==0) return ADMUX&0x0F;

  else if(addr==1) return ADMUX>>6;

  else if(addr>=2&&addr<=13) return pinState[addr];

  else if(addr>=18&&addr<=29) return pinPWM[addr-16];

  else if(addr==32) return syncMode;

  else if(addr==33) return syncSource;

  else if(addr==34) return static_cast<byte>(syncLevel>>2);

  else if(addr==35) return syncOffset;

  else if(addr==36) return reinterpret_cast<byte*>(&packetSize)[0];

  else if(addr==37) return reinterpret_cast<byte*>(&packetSize)[1];

  return 0;

}

void setPinState(byte pin) {

  switch(pinState[pin]) {

  case 0: /* input */

    pinMode(pin,INPUT);

    break;

  case 1: /* input_pullup */

    pinMode(pin,INPUT_PULLUP);

    break;

  case 2: /* force low */

    pinMode(pin,OUTPUT);

    digitalWrite(pin,LOW);

    break;

  case 3: /* force high */

    pinMode(pin,OUTPUT);

    digitalWrite(pin,HIGH);

    break;

  case 4: /* PWM */

    analogWrite(pin,pinPWM[pin]);

    break;

  }

/* Process the ADC interrupt */

ISR(ADC_vect) {

  static byte cnt=0;

  static unsigned int old_sample=0;

  if(++cnt==DECIMATION_FACTOR) cnt=0;

  if(cnt==0) {

    unsigned int sample=ADCL|(ADCH<<8);

/* Check trigger conditions */

    if(syncSource==0) { /* trigger by the analog input */

      if(syncMode==1&&sample>=syncLevel&&old_sample<syncLevel) triggered=true;

      else if(syncMode==2&&sample<=syncLevel&&old_sample>syncLevel) triggered=true;

      old_sample=sample;

    }

    else { /* trigger by a digital input */

      unsigned int s=digitalRead(syncSource);

      if(syncMode==1&&s==HIGH&&old_sample==LOW) triggered=true;

      else if(syncMode==2&&s==LOW&&old_sample==HIGH) triggered=true;

      old_sample=s;

    }

    adcBuffer[adcWriteIndex++]=sample;

  }

}




Необходимо добавить материалы...
Результат опроса Результаты Все опросы нашего сайта Архив опросов
Всего голосовало: 373



          

Радио для всех© 2024