Analog Discoveri 2 je korisna alatka za otklanjanje grešaka u projektima ili prilikom dizajna kod koji se koriste razni tipovi signala, a može se koristiti i uz WaveForms SDK za izradu automatiskih ali i testova specijalne namene. Sledeći primer je prikaz aplikacije ispitivača kontrolera motora koju koristi kupac koji proizvodi elektronske uređaje namenjene širokoj potrošnji. U ovoj konkretnoj aplikaciji više motora je korišćeno u sistemu gde je njihova funkcionalnost direktno uticala na sigurnost krajnjeg korisnika, pa je bilo neophodno opsežno testiranje.

Kao primer urađenog ovde je pokazan način kreiranje C/C++ projekta za Analog Discovery 2 pomoću WaveForms SDK, kao i njegovo korišćenje za otklanjanje grešaka na H-Bridge DC motor kontroleru kao i na kontroleru step motora, oba korišćena u automatskoj ljuljaški za bebe.

Preduslovi
Hardver

  • Analog Discovery 2 – USB osciloskop, logički analizator i multi-funkcionalan instrument

Softver

  • WaveForms – besplatan softver
  • Visual Studio Code ili neki drugi editor po izboru
  • C/C++ kompajler (obično dolazi uz korišćeni IDE)
  • MATLAB, Python ili bilo koji drugi program za vizuelizaciju podataka po izboru – opciono

Napomena: Prilikom instalacije WaveForms instalira se i WaveForms SDK, što je neophodno za realizaciju ovog projekta.

Testiraćemo i ispravljati greške u postojećem Aurduino projekatu za kontrolu rada motora. Oprema se sastoji od DC motora sa četkicama i step motora, uz odgovarajuće drajvere, dva potenciometra i leč hal-efekt senzora, što sve zajedno kontroliše Arduino mikrokontroler. Jedan potenciometar podešava brzinu DC motora a drugi zadaje poziciju step motora. Hall-efekt senzor menja smer okretanja motora na svakoj rastućoj/padajućoj ivici. Pojednostavljen dizajn uređaja, šema i Arduino kod se mogu preuzeti sa linka.

Izrada samog projekta kontrole rada motora prevazilazi temu ovog teksta, gde ćemo se baviti isključivo konceptima testiranja koji se mogu koristi i u drugim projektima. Da bi isporobali dati primer, jednostavno povežite vaš Analog Discovery 2 na bilo koju jednostavnu aplikaciju upravljanja rada motora ili uz male modifikacije iskoristite delove koda koje ćemo objasniti u nekoj vašoj aplikaciji.

Podešavanje hardvera
U projektu kojeg želimo da dibagujemo kao primer, H-Bidge DC motor drajver i step drajver su kontrolisani jednim Arduino UNO-om, u skladu sa ma sa dva potenciometra. Jedan potenciometar reguliše brzinu DC motora, dok drugi zadaje poziciju step motora. Hall-efekt senzor šalje interapt signal MCU-uon svakog poluokreta DC motora, a smer rotacije se u tom trenutku menja.

Da bi testirali sistem potrebno je prvo izmeriti vrednosti poteciometara, nakon toga vrši se testiranje oba motora slanjem digitalnih kontrolnih signala drajverima, dok je MCU ugašen, a nakon toga se MCU opet uključuje i snima se izlazni signal.

Da bi očitali vrednosti napona na potenciometrima, koriste se dva ulazna analoga kanala. Za upravljanje motor drajverima i za očitavanje izlaznih signala sa MCU-a koriste se digitalne I/O linije. Slika 1 pokazuje kako je izvedeno ožičenje.

Napomena: Bilo koji digitalni I/O se može povezati na bilo koji pin MCU/drajvera, samo je bitno beležiti konekcije, kako bi se pravilno koristile u kodu.

Napomena: Dijagram povezivanja samo pokazuje veze između Analog Discovery 2 i ostalih uređaja.

Stvaranje projekta
Kada se C/C++ projekat kreira, potrebno je kopirati dwf.lib iz WaveForms instalacione biblioteke, WaveFormsSDK\lib\x86 poddirektorijum i dwf.h fajl iz WaveForms instalacione biblioteke i poddirektorijum WaveFormsSDK\inc u direktorijum projekta. Inkludujte dwf.h  fajl u izvorni kod i linkujte dwf.lib biblioteku u vaš projekat. Na ovaj način moći ćete da koristite funkcije aveForms SDK.

Povezivanje i uklanjanje Analog Discovery 2
Da bi se Analog Discovery 2 koristio, mora biti povezan. Da bi se proverila bezbednost rada uređaja, program mora proveriti da li je Analog Discovery 2 povezan sa PC računarom. Ako jeste, on mora proveriti i da li je povezan i još neki uređaj. U slučaju da je Analog Discovery 2 detektovan hendler uređaja može preći na sledeće funkcije.

Funkcije za kontrolu različitim instrumentima
Sledeće funkcije se koriste za kontrolisanje instrumenata u ovom primeru. Ostale funkcije možete pogledati u priručniku koji prati sam uređaj.

Kada program završi sa korišćenjem Analog Discovery 2, mora se otkačiti od njega kako ne bi smetao drugom softveru da ga koristi.

//initialization function for AD2
bool init_AD2(HDWF *hdwf, char *error)
{
// detect if AD2 is connected or not
int nr_of_connected_dev;
FDwfEnum(devidDiscovery2, &nr_of_connected_dev);
if (nr_of_connected_dev == 0)
{
strcpy(error, „Error: Analog Discovery 2 is not connected“);
error[strlen(„Error: Analog Discovery 2 is not connected“)] = 0;
return false;
}
 
// detect if AD2 is used by other program or not (select the unused one if there are more devices)
int unused_dev_index = -1;
for (int i = 0; i < nr_of_connected_dev; i++)
{
int device_is_used;
FDwfEnumDeviceIsOpened(i, &device_is_used);
if (!device_is_used)
{
unused_dev_index = i; //save free device index
break;
}
}
if (unused_dev_index == -1)
{
strcpy(error, „Error: all connected Analog Discovery 2s are in use“);
error[strlen(„Error: all connected Analog Discovery 2s are in use“)] = 0;
return false;
}
 
// connect to the unused AD2
if (!FDwfDeviceOpen(unused_dev_index, hdwf))
{
strcpy(error, „Error: can’t connect to Analog Discovery 2“);
error[strlen(„Error: can’t connect to Analog Discovery 2“)] = 0;
return false;
}
return true;
}

Pre toga, svi korišćeni instrumenti se moraju vratiti na podrazumevana stanja u skladu sa reset funkcijom.

//reset every used instrument and close device
void close_AD2(HDWF hdwf)

{
FDwfDigitalIOReset(hdwf); //reset digital i/o
FDwfAnalogInReset(hdwf); //reset analog in
FDwfDigitalOutReset(hdwf); //reset digital out
FDwfDigitalInReset(hdwf); //reset digital in
FDwfDeviceClose(hdwf); //close device
return;
}

Statički I/O
Za kontrolu stanja digitalnih I/O pinova kod Analog Discovery 2 koristi se funkcija FDwfDigitalIO. Funkcija je slična Arduinovoj funkciji digitalWrite(): kao ulazne parametre dobija broj pina i logičko stanje i setuje dati digitalni I/O na željenu vrednost.

Prvi korak je podesiti izlazne pinove na željena stanja. Funkcija ne sme menjati vrednosti na drugim pinovima, samo na traženom. U sledećem koraku postavlja se maska na promenjive koje sadrže informaciju o pinu, kao i na one koje čuvaju stanje izlaznih pinova. Nova vrednost se vraća Analog Discovery 2 uređaju.

//write a value on a specific digital i/o pin
void digital_write(HDWF hdwf, int pin, int value)
{
// get current state
unsigned int previous_mask, previous_value;
FDwfDigitalIOOutputEnableGet(hdwf, &previous_mask); //get current pin setup
FDwfDigitalIOInputStatus(*hdwf, &previous_value); //get current pin state
 
//set new mask
unsigned int power = 1; //power = 2^pin
for (int i = 0; i < pin; i++)
{
power *= 2;
}
previous_mask |= power;
 
//set new state
if (value != 0)
{
previous_value |= power;
}
else
{
previous_value &= ~power;
}
 
//output new values
FDwfDigitalIOOutputEnableSet(hdwf, previous_mask); //set pin setup FDwfDigitalIOOutputSet(hdwf, previous_value); //set pin state
return;
}

Voltmetar
Da bi se očitali naponi u voltima kod Analog Discovery 2 sekoristi funkcija FDwfAnalogIn. Funkcija je slična Arduino funkciji analogRead() uz par modifikacija: funkcija očitava vrednost sa korišćenog osciloskopskog kanala i opciono prosečan broj semplova.

Prvo se podese kanal, opseg i ofset, onemogući se triger, prikupe se uzorci i uproseče, ako je neophodno.

//read voltage on a given analog channel
float analog_read(HDWF hdwf, int channel, int average = 10)
{
//configure the oscilloscope channel–; //channels start from 0
FDwfAnalogInChannelEnableSet(hdwf, channel, true); //channel is enabled
FDwfAnalogInChannelRangeSet(hdwf, channel, -6); //a range of -6 to 6 volts
FDwfAnalogInChannelOffsetSet(hdwf, channel, 0); //offset is 0V
FDwfAnalogInConfigure(hdwf, false, false); //no auto trigger
Wait(2000); //wait 2 seconds  
//average samples
float sum = 0;
for (int i = 0; i < average; i++)
{
FDwfAnalogInStatus(hdwf, false, NULL); //wait for acquisition
double temporal_voltage;
FDwfAnalogInStatusSample(*hdwf, channel, &temporal_voltage); //sample acquisition
sum += temporal_voltage;
Wait(100);
}
sum /= average; //calculate average
return sum;
}

Logički analizator
Za prikupljanje digitalnih podataka Analog Discovery 2-om koristi se FDwfDigitalInfunctions. Funkcija mora da je u mogućnosti da snimi definisani broj uzoraka u matricu, gde svaka kolona predstavlja digitalnu ulaznu liniju. Funkcija mora da može da signalizira ukoliko dođe do gubitka ili korupcije podataka.

Na početku, statički I/O, logički analizator i patern generator moraju biti resetovani na svoje predefinisane vrednosti. Nakon toga potrebno je postaviti učestanost uzorkovanja i izlazni format podataka. Kada se koristi Analog Discovery 2, preporučeni format podataka je 16 bita po reči, jer uređaj ima 16 digitalnih I/O linija.

Akvizicija se trigeruje rastućom ili opadajućom ivicom na bilo kom pinu, ali može biti generisana tajmerom, kako bi se zabeležio period bez promene vrednosti signala. (korisno kod dibagovanja)

Nakon pokretanja instrumenta, podaci se sve vreme beleže u baferu. U petlji, stanje bafera se sve vreme snima u izlaznom nizu podataka, pri čemu se vrši provera ispravnosti podataka i alarmira u slučaju gubitka podataka. Kada se postigne željeni broj uzoraka, izlazi se iz petlje.

Najzad se formira finalni zapis i ispravljaju moguće greške.

//record digital signals
int digital_read(HDWF *hdwf, bool **digital_data, unsigned int *buffer_size)
{
//reset digital instruments
FDwfDigitalIOReset(hdwf); //reset digital i/o
FDwfDigitalOutReset(hdwf); //reset digital out
FDwfDigitalInReset(*hdwf); //reset digital in
 
//create array for results
unsigned short *unformatted_data = (unsigned short )malloc(buffer_size * sizeof(unsigned short));
 
//set up the instrument
FDwfDigitalInAcquisitionModeSet(hdwf, acqmodeRecord); //record mode
double internal_frequency;
FDwfDigitalInInternalClockInfo(hdwf, &internal_frequency); //get clock speed
FDwfDigitalInDividerSet(hdwf, internal_frequency / 100000); //sample rate: 100kHz
FDwfDigitalInSampleFormatSet(hdwf, 16); //data formatting in 16 bits
 
//set up the trigger
FDwfDigitalInTriggerPositionSet(*hdwf, *buffer_size); //nr of aquisitions after the instrument is triggered
FDwfDigitalInTriggerSourceSet(hdwf, trigsrcDetectorDigitalIn); //trigger source: digital inputs
FDwfDigitalInTriggerAutoTimeoutSet(hdwf, 10.0); //trigger timeout: 10s
FDwfDigitalInTriggerSet(hdwf, 0, 0, 0xFFFF, 0xFFFF); //triggered if any pin changes (rising/falling edge)  

//start the aquisition
FDwfDigitalInConfigure(hdwf, false, true);
 
//wait for trigger/auto aquisition
int current_sample_count = 0, current_available_sample_count, current_lost_sample_count, current_corrupt_sample_count;
bool lost_flag = false, corrupt_flag = false;
STS sts;
while (current_sample_count < buffer_size)
{
FDwfDigitalInStatus(hdwf, 1, &sts); //check instrument state  
//skip this iteration, if recording hasn’t started yet
if (current_sample_count == 0 && (sts == DwfStateConfig || sts == DwfStatePrefill || sts == DwfStateArmed))
{
continue;
}  

//check buffer state
FDwfDigitalInStatusRecord(*hdwf, &current_available_sample_count, &current_lost_sample_count, &current_corrupt_sample_count);  

//count lost samples
current_sample_count += current_lost_sample_count;  

//check FIFO overflow
if (current_lost_sample_count != 0)
{
lost_flag = true;
}
if (current_corrupt_sample_count != 0)
{
corrupt_flag = true;
}  

//check data availability
if (current_available_sample_count == 0)
{
continue; //if no data is available, skip this iteration
}  

//if more data is available, then the buffer size, limit it
if (current_sample_count + current_available_sample_count > *buffer_size)
{
current_available_sample_count = buffer_size – current_sample_count; }  

//get samples
FDwfDigitalInStatusData(hdwf, &unformatted_data[current_sample_count], 2 * current_available_sample_count);
 
//count saved samples
current_sample_count += current_available_sample_count;
}
 
//stop the aquisition
FDwfDigitalInConfigure(*hdwf, false, false);
 
//format data
for (int i = 0; i < *buffer_size; i++)
{
digital_data[0][i] = unformatted_data[i] & 0x0001;
digital_data[1][i] = unformatted_data[i] & 0x0002;
digital_data[2][i] = unformatted_data[i] & 0x0004;
digital_data[3][i] = unformatted_data[i] & 0x0008;
digital_data[4][i] = unformatted_data[i] & 0x0010;
digital_data[5][i] = unformatted_data[i] & 0x0020;
digital_data[6][i] = unformatted_data[i] & 0x0040;
digital_data[7][i] = unformatted_data[i] & 0x0080;
digital_data[8][i] = unformatted_data[i] & 0x0100;
digital_data[9][i] = unformatted_data[i] & 0x0200;
digital_data[10][i] = unformatted_data[i] & 0x0400;
digital_data[11][i] = unformatted_data[i] & 0x0800;
digital_data[12][i] = unformatted_data[i] & 0x1000;
digital_data[13][i] = unformatted_data[i] & 0x2000;
digital_data[14][i] = unformatted_data[i] & 0x4000;
digital_data[15][i] = unformatted_data[i] & 0x8000;
}
 
//signal errors
if (lost_flag)
{
return 1;
}
if (corrupt_flag)
{
return 2;
}
return 0;
}

Patern generator
Za generisanje PWM signala koristi se FDwfDigitalOut funkcija. Prvo se omogući signal na željenoj digitalnoj I/O liniji, pa se postavlja učestanost signala. Radni cilus je podeljen na dva vremenska okvira “High time” i “Low time”. Dati kod inicijalizuje PWM signal na digitalnoj I/O liniji i podiže “duty cycle” od 0% do 100%, sa porastom od 1% svakih 50ms. Nakon toga zaustavlja signal.

//set up the pwm signal
FDwfDigitalOutEnableSet(hdwf, CH, true); //enable channel FDwfDigitalOutTypeSet(hdwf, CH, 0); //set type to previous_value
double internal_clock_frequency;
FDwfDigitalOutInternalClockInfo(hdwf, &internal_clock_frequency); //get clock frequency
unsigned int range_min, range_max;
FDwfDigitalOutCounterInfo(hdwf, CH, &range_min, &range_max); //get counter range
int frequency = 1000; //1KHz pwm frequency
unsigned int divider = ceil(internal_clock_frequency / frequency / range_max);
FDwfDigitalOutDividerSet(*hdwf, CH, divider); //set pwm frequency
unsigned int pulse = round(internal_clock_frequency / frequency / divider);
 
//varying the speed of the motor
for (int i = 0; i < 100; i++)
{
unsigned int max = pulse * i / 100;
unsigned int min = pulse – max;
FDwfDigitalOutCounterSet(hdwf, CH, min, max); //set duty cycle FDwfDigitalOutConfigure(hdwf, true); //start the instrument
Wait(50);
}
 
//stop the motor
FDwfDigitalOutEnableSet(*hdwf, CH, false);

Kompletan kod se može preuzeti sa linka. ad2_motor_debugger.zip je tu, sa dwf.h heder datotekom koji objedinjuje različite operacije, main.cpp izvorni kod koji objedinjuje ceo projekat, definiše njegovu strukturu i sadrži dva skripta, jedan u Pyton-u, a druga je MATLAB skripta za prikaz sačuvanih podataka.

Projekat
Na početku projekta, definiše se povezivanje. Njegovim definisanjem na početku koda, lakše ga je kasnije modifikovati ukoliko se izmeni način povezivanja dibagera na MCU ili motor drajver. Podrazumevano stanje pina za enejblovanje motor drajvera i napomena o korišćenim bibliotekama se ovde definišu.

//define connections
//digital i/o
#define MCU_RESET 0
#define DC_AIN1 1
#define DC_AIN2 2
#define DC_STBY 3
#define DC_PWMA 4
#define STEP_EN 5
#define STEP_MS1 6
#define STEP_MS2 7
#define STEP_DIR 8
#define STEP_STEP 9
#define HALL 10
//analog in
#define POT_SPEED 1
#define POT_POS 2
 
//define defaults
#define DC_ON HIGH //state of the stby pin when the motor is on
#define STEP_ON LOW //state of the en pin when the stepper is on
 
/*———————————————-*/
 
//include headers needed for input/output
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
 
//include the DWF header and the custom header for this project
#include „dwf.h“
#include „AD2_motor_debugger.h“

U glavnoj “main” funkciji prvi korak je povezati Analog Discovery 2. Ako se ne može povezati, program se mora zaustaviti, kako se ne bi pozivale naredne funkcije. Funkcija init_AD2() je detaljno objašnjena pod: “Functions for Controlling Different Instruments”.

//main function
int main(int argc, char **argv)
{
HDWF hdwf; //variable for instrument handle
char error[512]; //variable for error messages
 
// initialize the Analog Discovery 2
if (!init_AD2(&hdwf, error))
{
printf(„%s\nPress any key to exit…“, error);
_getch();
return 0;
}
printf(„Analog Discovery 2 is connected\n“);
Wait(2000); //wait 2 seconds

Da bi izvršio test motor drajvera, mikrokontroler se mora ugasiti postavljajući njegov RESET pin na “low”. Nakon toga, moguće je pročitati napon na voltmetru, a funkcije digital_write() i funkcija za generisanje PWM signala mogu se koristiti za testiranje oba motora prilikom prmenjivih brzina rada.

//main function
printf(„\nReading controls and testing motors:\n“);
 
// turn the microcontroller off to read/test peripherals
printf(„\tThe MCU is turned off\n“);
digital_write(&hdwf, MCU_RESET, LOW);
 
//read voltages on potentiometers
printf(„\tMeasuring voltages on potentiometers:\n“);
float voltage_speed = analog_read(&hdwf, POT_SPEED);
printf(„\t\tVoltage on the potentiometer controlling the speed: %2.2fV\n“, voltage_speed);
float voltage_position = analog_read(&hdwf, POT_POS);
printf(„\t\tVoltage on the potentiometer controlling the position: %2.2fV\n“, voltage_position);
 
//testing the DC motor
printf(„\tTesting the DC motor\n\t\tClockwise direction…\n“);
drive_DC(&hdwf, DC_AIN1, DC_AIN2, DC_STBY, DC_ON, DC_PWMA, 1);
printf(„\t\tCounter-clockwise direction…\n“);
drive_DC(&hdwf, DC_AIN1, DC_AIN2, DC_STBY, DC_ON, DC_PWMA, 0);
 
//testing the stepper
printf(„\tTesting the stepper motor\n\t\tClockwise direction…\n“);
drive_STEP(&hdwf, STEP_MS1, STEP_MS2, STEP_EN, STEP_ON, STEP_DIR, STEP_STEP, 1);
printf(„\t\tCounter-clockwise direction…\n“);
drive_STEP(&hdwf, STEP_MS1, STEP_MS2, STEP_EN, STEP_ON, STEP_DIR, STEP_STEP, 0);

Da bi zabeležili signale koji dolaze iz MCU-a, on mora biti uključen, setovanjem RESET pina na “high”. Nakon toga se mora specificirati broj uzoraka koji se zapisuje (digital_size) i matrica odgovarajuće veličine (digital_data) mora biti kreirana pre zapisisvanja podataka. Nakon akvizicije, proverava se gubitak ili korupcija podataka, i tek nakon toga se podatak može sačuvati u fajlu.

printf(„\nReading the output signals of the MCU:\n“);
 
// turn the microcontroller on to read/test it’s signals
printf(„\tThe MCU is turned on\n“);
digital_write(&hdwf, MCU_RESET, HIGH);
 
// record all signals coming from the MCU and the HALL effect sensor
printf(„\tRecording digital signals\n“);
 
//allocate space for the data
unsigned int data_size = 1000000; //specify the number of samples
bool **digital_data; //buffer for data
digital_data = (bool **)malloc(16 * sizeof(bool *));
for (int i = 0; i < 16; i++)
{
digital_data[i] = (bool *)malloc(data_size * sizeof(bool));
}
 
int read_state = digital_read(&hdwf, digital_data, &data_size); //read
 
//check for data loss/corruption
if (read_state == 1)
{
printf(„\t\tData was lost due to FIFO overflow\n“);
}
if (read_state == 2)
{
printf(„\t\tData was corrupted due to FIFO overflow\n“);
}
 
//save data in a file
if (!save_data(digital_data, data_size, error))
{
printf(„%s\nPress any key to exit…“, error);
_getch();
return 0;
}
printf(„\tThe recorded data was saved\n“);

Kada se program završi, Analog Discvery 2 se mora zatvoriti, kako bi bio dostupan drugom softveru.

// close opened device
printf(„\nClosing the connected device“);
close_AD2(&hdwf);
return 0;

Rezultat testa se prikazuje na ekranu konzole sa porukom o trenutnom radu uređaja.

Podaci eksportovani u datoteku mogu se vizuelizovati pokretanjem plotting.m MATLAB skripte ili plotting.py Python programom, ili možete napisati sopstveni program za iscrtavanje podataka.

Finalni komentari
Za dodatne informacije i projekte sa Analog Discovery 2 uređajem, posetite Resource Center. Za tehničku podršku posetite Digilent Forums.

Aleksandar Dakić
Follow me