Table of Contents

Function Generator

Autor: Andrei BIU-PÎSLARU (101400)

Introducere

Scopul acestui proiect este explorarea capacitatii placii de dezvoltare Arduino UNO de a functiona ca un generator de functii - un astfel de aparat este foarte util in dezvoltarea circuitelor electronice. Astfel, produsul rezultat va trebui sa fie capabil sa produca functii de semnal (de exemplu sinusoide) si PWM.

Fata de alte placi Arduino, UNO nu are un DAC.

Astfel este necesar sa se foloseasca alternative (de exemplu, un DAC extern).

Proiectul este gandit astfel incat sa poate fi transformat intr-un shield Arduino care sa fie relativ accesibil din punct de vedere financiar (astfel sa se utilizeze cat mai putie componente externe si cat mai ieftine) si usor de realizat (chiar si acasa, in principiu) avand capacitati si performante satisfacatoare pentru un hobbyist (nu pentru uz intens) si o interfata usor de folosit (dar in acelasi spirit de a folosi putine componente).

Descriere

Proiectul este un generator de functii capabil sa genereze sinusoide, semnale trapezoidale, triunghiulare si dinte de fierastrau (“sawtooth”) cu frecventa de 1Hz pana la 4.5kHz ajustabila cu pasi de 1Hz, amplitudine intre 1 si 5V cu pasi de 100mV, respectiv PWM cu unul sau doua canale cu frecventa comuna intre 1Hz si 100kHz ajustabila cu pasi tot de 1Hz si un factor de umplere setabil per canal de la 0% la 99% in pasi de 1%, cu o amplitudine data de tensiunea de alimentare. Astfel exista 3 iesiri, una de functie de semnal si doua pentru cele doua canale de PWM. Aparatul poate functiona fie in modul waveform, fie in modul PWM (nu poate genera ambele in acelasi timp).

Iesirile, spre deosebire de un generator de functii normal sunt amplificate in putere. Etajul de filtrare pentru functii de semnal poate alimenta fara perturbatii rezistente de 400Ω la amplitudinea maxima, iar partea de PWM este amplificata de un MOSFET driver, deci poate fi direct conectata la sarcini inductive mari (util pentru testarea circuitelor ce contin tranzistori de putere).

Toate setarile sunt introduse printr-o interfata alcatuita dintr-un LCD 16×2 si 5 butoane push (ce au functiile de up, down, left, right si select/ok) folosite pentru a naviga pe ecran si a selecta / introduce valori.

Chiar daca intervalul de frecvente este ajustabil intr-un spectru continuu cu pasi de 1Hz, nu toate frecventele sunt disponibile din cauza limitarilor software si hardware. Daca este introdusa o valoare care este indisponibila, se va aplica si afisa cea mai apropiata valoare disponibia automat.

Hardware Design

Lista de componente folosite:

Solutia gasita pentru a suplini lipsa unui DAC intern a fost utilizarea unei solutii software pe Arduino + realizarea unui DAC R2R. Aceasta alternativa a fost aleasa in primul rand pentru ca este cea mai potrivita raportata la tema proiectului - explorarea capacitatilor placii Arduino - dar si pentru costul foarte redus. DAC-ul are o rezolutie de 8 biti si este conectat la pinii 0-7 aferenti portului PD al μC-ului Atmega328P (si atfel se realizeaza transformarea in tensiune a valorii scrise pe port in registrul aferent din software). Pentru partea de PWM lucrurile sunt mai simple deoarece exista suport hardware in μC.

In final, pentru partea de functie de semnal s-a folosit un filtru activ Sallen-Key cu factor de amplificare unitar pentru imbunatatirea calitatii formei de unda rezultate la iesirea DAC-ului, iar pentru PWM IC-ul specificat. Alegerea unui LCD I2C este data de utilizarea extensiva a pinilor placii Arduino UNO in cadrul acestui proiect.

Alimentarea intregului aparat se face prin conectorul de alimentare coaxial al placii Arduino. Tensiunea nu trebuie sa aiba o valoare precisa, dar schimbarea ei va afecta amplitudinea PWM (care este data de tensiunea de alimentare).

Software Design

Realizarea laturii software urmareste cele doua parti principale (vizibile si in schema bloc si in cea electrica): partea de generare de semnale si partea de interfata cu utilizatorul.

Signal

Generarea semnalelor (implementarea functionalitatii de generator de functie) se face in biblioteca Signal (Signal.hpp) printr-o clasa care ofera toate functiile necesare. In plus este stabilit un API foarte simplu prin care se pot oricand adauga forme de unda noi.

Clasa Signal expune urmatoarea structura:

Generarea functiei de semnal se face prin iterarea in tabloul de valori al functiei de semnal prin parcurgerea circulara element cu element - trecerea de la o iteratie la alta se face printr-o intrerupere la intervale precise cu ajutorul Timer/Counter1 - si scrierea valorii pe portul D. Generarea PWM se face cu acelasi timer configurat in modul Fast PWM.

Datele unei functii de semnal sunt reprezentate de un tablou unidimenional de 64 de octeti avand valori de la 0 la 255 corespunzator amplitudinii instantanee din acel moment intre 0 si 5V. Tabloul este stocat in memoria de program pentru a nu lua din memoria SRAM limitata. Pentru a declara o functie de semnal (waveform) se foloseste API - ul folosit, de exemplu pentru declararea unei functii “CastleWave”:

WAVEFORM_DECL(Castle) = { /* 64 Valori ... */ };

LCDInterface

Cea de-a doua biblioteca cu care vine partea de software este cea care se ocupa de interfata cu utilizatorul. Bilioteca LCDInterface are rolul de a crea o interfata virtuala (logica) peste un API de comunicare cu un LCD I2C si 5 butoane de selectie / navigare (up, down, left, right, ok/select). Biblioteca introduce 3 clase / structuri / entitati care realizeaza interfata logica:

Prin “apasarea” butoanelor virtuale se muta cursorul care indica casuta curenta de pe ecran. Acest lucru nu este insa valabil cand este selectata o celula de tip 'DataBox' pentru introducere de date. Atunci apelurile catre functiile de butoane nu mai muta cursorul; in schimb functioneaza doar cele de 'up' si 'down' care realizeaza ciclarea prin valorile celulei.

Toate pozitiile din biblioteca sunt specificate prin structura:

struct Position { byte row, column };

In afara de functii built-in din mediul Arduino am folosit biblioteca LiquidCrystal_I2C: Link Github

FunctionGenerator

Acesta este fisierul .ino in care este cod specific proiectului, construit pe baza celor doua biblioteci anterior descrise. Aici este pastrata starea aparatului, este gestionata apasarea butoanelor (prin functia loop specifica Arduino si intreruperi de tip pin change - PCINT; mai precis, in ISR - uri se detecteaza si se salveaza ce buton a fost apasat, iar apoi in loop se face pooling pentru a apela metoda corespunzatoare de press din LCDInterface; in final toata logica suplimentara la selectia diferitelor elemente de pe ecran este tratata prin handlerul dat ca paramentru metodei LCDInterface::pressSelect) si sunt realizate initializarile necesare (functia setup specifica Arduino).

Utilizare

Utilizatorul aparatului trebuie doar sa seteze modul dorit si parametrii specifici fiecarui mod de functionare (waveform, PWM). Acest lucru se face prin navigarea si selectia pe ecran cu ajutorul celor 5 butoane. Intotdeauna exista o casuta curenta pe ecran (similar cu un element de interfata cu utilizatorul dintr-un GUI - de exemplu un buton - atunci cand se face navigarea cu tab) care este evidentiata printr-un cursor de tip underscore (“_”) ce este miscat prin cele 4 taste de directie.

Dupa pornirea aparatului, cursorul nu este afisat.

Este necesar sa se apese o tasta de directie pentru a fi mutat cursorul pentru prima data si a fi afisat. Acesta este situat le pornire mereu in casuta din coltul stanga-sus al ecranului.

Introducerea parametrilor / setarilor se face in casutele corespunzatoare astfel:

Casutele alaturate care permit introducerea de cifre formeaza impreuna un numar. Chiar daca se poate schimba doar valoarea unei cifre la un moment dat, valoarea citita de aparat este formata din toate cifrele.

Daca este introdusa o valoare invalida sau indisponibila, se considera si afiseaza cea mai apropiata valoare valida.

Descrierea completa a interfetei se regaseste in imaginile de mai jos:

In imagine e prezentata structura ecranului cand modul este Waveform, outputul este dezactivat (O arata ca prin apasare se activeaza semnalul, X semnifica dezactivarea), iar tipul de semnal este Sawtooth (SAW - Sawtooth wave, SIN - Sine wave, TRI - triangle wave, TPZ - trapezoidal wave).

In imagine e prezentata structura ecranului cand modul este PWM, outputul este activat (O arata ca prin apasare se activeaza semnalul, X semnifica dezactivarea) si este activ un singur canal (SCH - Single channel mode, DCH - dual channel mode).

Testare & Demo

Pentru testarea proiectului am realizat un prototip pe breadboard utilizand exact componentele specificate in sectiunea de design hardware. In plus am utilzat urmatoarele instrumente pentru testare:

Prototipul rezultat este ilustrat in imaginile de mai jos:

Exemple de moduri de functionare ale dispozitivului:

Live demo pentru modul Waveform: Video

Live demo pentru modul Waveform: Video

Concluzii & Future Work

Proiectul poate primi o serie de imbunatatiri fata de forma descrisa / deja implementata pentru a extinde capacitatile acestuia:

  1. Frecventa maxima obtinuta in modul waveform este limitata in primul rand de software. Overheadul unei intreruperi este mare pentru astfel de aplicatii, mai ales in contextul in care codul dat de compilator nu este prea eficient pentru ISA-uri. In particular este pesimist in ceea ce priveste utilizarea registrilor si simte nevoia sa faca prea multe salvari pe stiva. Schimbarea codului din ISA-ul pentru waveform cu unul in limbaj de asamblare (inline assembly + ISR_NAKED) poate creste frecventa pana la 5kHz (acest lucru inca nu a fost realizat deoarece programul functiona incorect cu toate variantele de cod incercat). Mai departe, rezervarea unor registrii globali poate aduce o dublare pana la 10kHz, dar acest lucru presupune abandonarea mediului Arduino si programarea in C fara a folosi nicio biblioteca externa (totul de la zero), inclusiv multe functii din implementarea libc pentru AVR (dintre cele precompilate).
  2. Filtrul Sallen-Key nu are un efect de filtrare suficient de bun pentru netezirea frecventelor mici. Ar fi nevoie de un filtru realizat cu trimmer programabil. Exista astfel de IC-uri, insa tind sa aiba o plaja mica de capacitante. In cazul aparatului ar fi nevoie de ceva in intervalul de cativa μF - sute de pF. Acest lucru poate fi realizat cu un decodor pe 3 biti (ultimii 3 pini liberi ai placii), de exemplu 74HC237 care sa comande MOSFET-uri de joasa putere precum 2N7000 care la randul lor sa cupleze condensatorii necesari. Aceasta poate permite o reducere a numarului de valori necesare pentru reconstruirea semnalului, deci la cresterea frecventei maxime (o limita realista este in jurul a 20kHz).

Cu toate acestea, raman suficiente limitari pe partea de generare de waveform care pot fi prea mari pentru un alt uz decat cel ocazional in regim de hobbyist. Pentru o performanta mult mai buna trebuie inlocuita intreaga parte de generare de functii de unda (hardware + parte din software) cu un DAC cu memorie interna (cu o interfata I2C sau SPI), sau chiar cu un IC dedicat pentru generare de semnale.

Dintre acestea, cel mai probabil varianta cu DAC-ul este mai avantajoasa din punct de vedere al costului si continua sa ofere flexibilitatea de a genera semnale oarecare definite in soft. Totusi aceste alternative ies intr-o anumita masura din scopul initial al proiectului - acela de a testa capacitatile intrinseci ale placii Arduino UNO de a functiona ca generator de functii de semnal.

Download & Resurse aditionale

Arhiva cu toate fisierele proiectului poate fi descarcata aici: Arhiva proiect

Alte resurse folosite pe parcursul dezvoltarii proiectului:

https://lastminuteengineers.com/i2c-lcd-arduino-tutorial/

https://www.electronics-tutorials.ws/combination/r-2r-dac.html

https://ocw.cs.pub.ro/courses/pm/lab/lab3-2021

https://www.arduino.cc/reference/en

Prezentarea pentru PM Fair: Function Generator