Operácie

Generátor harmonického signálu: Rozdiel medzi revíziami

Zo stránky SensorWiki

StudentMIPS (diskusia | príspevky)
updated not clear enough explanations
Balogh (diskusia | príspevky)
dBez shrnutí editace
 
(44 medziľahlých úprav od jedného ďalšieho používateľa nie je zobrazených)
Riadok 1: Riadok 1:
Záverečný projekt predmetu MIPS / LS2026 - '''Oleksandr Mykyta'''
Záverečný projekt predmetu MIPS / LS2026 - Oleksandr Mykyta


== Zadanie ==
== Zadanie ==


Ulohou je generovat harmonicky signal bez pouzitia funkcii sin() alebo cos(). Namiesto toho sa ma pouzit oscilator realizovany ako prenosova funkcia:
Na generovanie harmonického signálu nepoužite funkcie sin(), resp. cos(), ale vytvorte oscilátor (v reálnom čase) ako prenosovú funkciu 1/((s*T)^2 + 1). Úlohou je zmerať  jeden bod frekvenčnej  charakteristiky systému 1/(s*T_osc+1) na frekvencii omega = 1/T. T = 0,5 sek.  Výstupom je signál s parametrami: A_0 + A_1*sin(omega*t + fi), kde A_0 = 128 a A_1 = 100. 


$$H(s) = \frac{1}{(s \cdot T)^2 + 1}$$


Zaroven je potrebne zmerat jeden bod frekvencnej charakteristiky systemu:
Úlohou bolo generovať harmonický signal bez použitia funkcií sin() alebo cos(). 
Na tento účel bol použitý oscilátor realizovaný ako prenosová funkcia:


$$H(s) = \frac{1}{s \cdot T + 1}$$
<code>
H(s) = 1 / ((s · T)^2 + 1)
</code>


na frekvencii ω = 1 / T, pricom T = 0,5 s.
Zároveň bolo potrebné zmerať jeden bod frekvenčnej charakteristiky systému:


Vystupny signal ma mat tvar:
<code>
H(s) = 1 / (s · T + 1)
</code>


$$A_0 + A_1 \cdot \sin(\omega t + \varphi)$$
pre frekvenciu:
<code>ω = 1 / T</code>,
kde
<code>T = 0.5 s</code>.
 
Výstupný signal má mať tvar:
 
<code>
A₀ + A₁ · sin(ωt + φ)
</code>


kde:
kde:
A₀ = 128
<code>A₀ = 128</code>,
A₁ = 100
<code>A₁ = 100</code>.
 
 
'''Literatúra:'''
* [http://ap.urpi.fei.stuba.sk/sensorwiki/index.php/Acrob_technical_description Dokumentácia k doske Acrob]
* [https://en.wikipedia.org/wiki/Harmonic_oscillator Harmonic oscillator]
* [https://en.wikipedia.org/wiki/Transfer_function Transfer function]
* [https://en.wikipedia.org/wiki/Euler_method Euler method]


__TOC__
__TOC__


== Analýza a opis riešenia ==
== Analýza a opis riešenia ==
 
Cieľom riešenia je vytvoriť sinusový signal bez použitia matematických funkcií sin() alebo cos(). Tento problém sa rieši pomocou diskretneho oscilátora, ktorý vychádza z diferenciálnej rovnice harmonického kmitania.
 
Základom je rovnica:
 
<code>
y'' + ω²y = 0
</code>
 
Táto rovnica popisuje harmonické kmity a jej riešením sú funkcie <code>sin()</code> a <code>cos()</code>. To znamená, že ak vieme túto rovnicu numericky riešiť, vieme generovať sinus.
 
=== Teoretický základ a odvodenia ===
 
==== Laplaceova transformacia ====
 
Vychadzajme z prenosovej funkcie systému:
 
<code>
H(s) = Y(s) / X(s) = 1 / (T · s + 1)
</code>
 
Po úprave:
 
<code>
Y(s) · (T · s + 1) = X(s)
</code>
 
<code>
T · s · Y(s) + Y(s) = X(s)
</code>
 
Použitím inverznej Laplaceovej transformacie (kde s predstavuje deriváciu) dostaneme:
 
<code>
T · dy(t)/dt + y(t) = x(t)
</code>
 
Týmto získame diferenciálnu rovnicu systému v časovej oblasti.
 
==== Diskretizacia ====
 
Mikrokontrolér pracuje v diskretnom čase, preto je potrebné nahradiť derivácie rozdielmi medzi vzorkami.
 
Pre druhú deriváciu použijeme aproximáciu:
 
<code>
y'' ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s²
</code>
 
Po dosadení do <code>y'' + ω²y = 0</code>:
 
<code>
y[n] = (2 / (1 + ω²T_s²)) · y[n−1] − (1 / (1 + ω²T_s²)) · y[n−2]
</code>
 
Táto rovnica predstavuje numerickú aproximáciu oscilátora, ale nie je ideálna z hľadiska stability amplitúdy.
 
==== Diskretny oscilátor (presný model) ====
 
Presnejší prístup vychádza z trigonometrických identít:
 
<code>
sin(A + B) = sin(A)cos(B) + cos(A)sin(B)
</code>
 
<code>
sin(A − B) = sin(A)cos(B) − cos(A)sin(B)
</code>
 
Po sčítaní:
 
<code>
sin(A + B) + sin(A − B) = 2 · sin(A) · cos(B)
</code>
 
Dosadením:


Cielom riesenia je vytvorit sinusovy signal bez pouzitia matematickych funkcii sin() alebo cos(). Tento problem sa riesi pomocou diskretneho oscilatora, ktory vychadza z diferencialnej rovnice harmonickeho kmitania.
<code>A = (n−1)θ</code>,
<code>B = θ</code>


### Laplaceova transformacia a prenosova funkcia
dostaneme:
Vychadzajme z nasej prenosovej funkcie H(s), co je v podstate len pomer vystupu k vstupu v Laplaceovej oblasti:


$$H(s) = \frac{Y(s)}{X(s)} = \frac{1}{T \cdot s + 1}$$
<code>
sin(nθ) + sin((n−2)θ) = 2 · cos(θ) · sin((n−1)θ)
</code>


Ak chceme vediet, ako sa to sprava v case, potrebujeme to transformovat naspat. Vynasobime rovnicu krizom:
Označením:


$$Y(s) \cdot (T \cdot s + 1) = X(s)$$
<code>y[n] = sin()</code>
$$T \cdot s \cdot Y(s) + Y(s) = X(s)$$


Teraz pouzijeme inverznu Laplaceovu transformaciu. Hlavne pravidlo tu je, ze premenna s sa v casovej domene sprava ako derivacia: $\mathcal{L}^{-1}\{s \cdot Y(s)\} = \frac{dy}{dt}$. Po aplikovani inverznej transformacie dostaneme diferencialnu rovnicu:
vznikne rekurentný vzťah:


$$T \cdot \frac{dy(t)}{dt} + y(t) = x(t)$$
<code>
y[n] = 2 · cos(θ) · y[n−1] − y[n−2]
</code>


Tymto sme si potvrdili, ze nasa prenosova funkcia v Laplaceovi a diferencialna rovnica v case su vlastne len dva pohlady na to iste.
Tento vzťah generuje stabilný sinusový signal bez zmeny amplitúdy.


### Diskretizácia
==== Výpočet parametrov ====
Mikrokontroler pracuje v diskretnom case, preto je potrebne nahradit derivacie rozdielmi medzi vzorkami (metoda konecnych diferencii).


Pouzie sa aproximacia druhej derivacie pre oscilator:
Platí:


$$y'' \approx \frac{y[n] - 2y[n-1] + y[n-2]}{T_s^2}$$
<code>
θ = ω · T_s
</code>


### Diskrétny oscilátor
kde:
Na generovanie stabilneho sinusu sa pouzije presny diskretny model zalozeny na trigonometrickej identite:
<code>T = 0.5 s</code> 
<code>ω = 1 / T = 2 rad/s</code> 
<code>SAMPLE_RATE = 1000 Hz</code> 
<code>T_s = 0.001 s</code>


$$\sin(n\theta) = 2\cos(\theta)\cdot\sin((n-1)\theta) - \sin((n-2)\theta)$$


Po oznaceni:
<code>θ = 0.002</code>


$$y[n] = \sin(n\theta)$$
==== Aproximacia cos() ====


dostaneme rekurentny vztah:
Keďže nie je dovolené použiť funkciu cos(), použije sa Taylorov rozvoj:


$$y[n] = 2\cos(\theta)\cdot y[n-1] - y[n-2]$$
<code>
cos(θ) 1 − θ²/2
</code>


Tento vztah generuje stabilny sinusovy signal bez zmeny amplitudy. Pre vypocet parametrov plati $\theta = \omega \cdot T_s$.
Z toho:


### Aproximácia cos()
<code>
Kezde nie je dovolene pouzit funkciu cos(), pouzie sa aproximacia (Taylorov rozvoj):
2cos(θ) ≈ 2 · (1 − θ²/2)
</code>


$$\cos(\theta) \approx 1 - \frac{\theta^2}{2}$$
==== Inicializácia oscilátora ====


Tento vyraz sa pouzie ako koeficient oscilatora v programe.
Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty:


### Systém 1 / (sT + 1) a koeficient alpha
<code>y1 = 1</code>,
Aby mikrokontroler vedel vyuzit system 1 / (sT + 1), musime ho previest do diskretneho casu pomocou Eulerovej metody. Derivaciu nahradime diferenciami:
<code>y2 = 1 − θ²/2</code>


$$\frac{dy}{dt} \approx \frac{y[n] - y[n-1]}{T_s}$$
==== Generovanie signalu ====


Ked to dosadime do diferencialnej rovnice:
Oscilátor generuje hodnoty v rozsahu:


$$T \cdot \frac{y[n] - y[n-1]}{T_s} + y[n] = x$$
<code>[-1, 1]</code>


Po uprave dostaneme:
Požadovaný výstup:


$$y[n] = \left(\frac{T_s}{T + T_s}\right) \cdot x + \left(\frac{T}{T + T_s}\right) \cdot y[n-1]$$
<code>
x = A0 + A1 · y
</code>


Ak si oznacime α = Ts / (T + Ts), tak to vyzera ovela krajsie:
kde:


$$y[n] = \alpha \cdot x + (1 - \alpha) \cdot y[n-1]$$
<code>A0 = 128</code> 
<code>A1 = 100</code>


Co je to iste ako:
==== Diskretizacia systému <code>1 / (sT + 1)</code> ====


$$y[n] = y[n-1] + \alpha \cdot (x - y[n-1])$$
Zo spojitej rovnice:


Pre dane hodnoty α ≈ 0,001996.
<code>
T · dy/dt + y = x
</code>
 
Použitím Eulerovej metódy:
 
<code>
dy/dt ≈ (y[n] − y[n−1]) / T_s
</code>
 
Po úprave:
 
<code>
y[n] = x − (T / T_s) · y[n] − (T / T_s) · y[n−1]
</code>
 
<code>
y[n] · (1 + (T / T_s)) = x − (T / T_s) · y[n−1]
</code>
 
<code>
y[n] = (T_s / (T + T_s)) · x + (T / (T + T_s)) · y[n−1]
</code>
 
Po zavedení:
 
<code>α = T_s / (T + T_s)</code>
 
dostaneme praktický tvar:
 
<code>
y[n] = α · x + (1 − α) · y[n−1]
</code>
 
<code>
y[n] = y[n−1] + α · (x − y[n−1])
</code>


=== Algoritmus a program ===
=== Algoritmus a program ===


Algoritmus programu vyuziva Timer1 prerusenie, v ktorom sa vypocitava oscilator a nasledne filter. Vystup sa posiela do PWM modulu a data cez UART.
Algoritmus programu využíva diskretny oscilátor a numericku aproximáciu systému <code>1 / (sT + 1)</code>. Základné výpočty prebiehajú v prerušení Timer1 s frekvenciou 1 kHz:
 
* výpočet oscilátora
* generovanie vstupu x
* výpočet výstupu systému
* výstup cez PWM
* odosielanie dát cez UART


<tabs>
<tabs>
<tab name="AVR C-code"><syntaxhighlight lang="c++" style="background: LightYellow;">
<tab name="main.c"><syntaxhighlight lang="c++" style="background: LightYellow;">
#define F_CPU 16000000UL
#define F_CPU 16000000 UL


#include <avr/io.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdio.h>
#include "uart.h"
#include "uart.h"


Riadok 126: Riadok 279:
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);


ISR(TIMER1_COMPA_vect)
ISR(TIMER1_COMPA_vect) {
{
     y = OSC_COEFF * y1 - y2;
     y = OSC_COEFF * y1 - y2;


Riadok 139: Riadok 291:
     OCR0A = (uint8_t)(y_sys);
     OCR0A = (uint8_t)(y_sys);


     printf("%d,%d\n", (int)x, (int)y_sys);
     printf("%d,%d\n", (int) x, (int) y_sys);
 
}
}


void timer1_init()
void timer1_init() {
{
     TCCR1B |= (1 << WGM12);
     TCCR1B |= (1 << WGM12);


Riadok 151: Riadok 303:


     TIMSK1 |= (1 << OCIE1A);
     TIMSK1 |= (1 << OCIE1A);
}
}


void pwm_init()
void pwm_init() {
{
     DDRD |= (1 << PD6);
     DDRD |= (1 << PD6);


     TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
     TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
     TCCR0B |= (1 << CS01);
     TCCR0B |= (1 << CS01);
}
}


int main(void)
int main(void) {
{
     uart_init();
     uart_init();
     stdout = &mystdout;
     stdout = & mystdout;


     pwm_init();
     pwm_init();
Riadok 182: Riadok 334:
     sei();
     sei();


     while (1)
     while (1) {}
     {
 
}
</syntaxhighlight></tab>
<tab name="uart.h"><syntaxhighlight lang="c++" style="background: LightYellow;">
#define set_bit(ADDRESS, BIT)(ADDRESS |= (1 << BIT))
#define clear_bit(ADDRESS, BIT)(ADDRESS &= ~(1 << BIT))
 
#ifndef UART_H_
#define UART_H_
 
#include <stdio.h>
 
#define BAUD_PRESCALE(((F_CPU / (BAUDRATE * 16 UL))) - 1)
 
void uart_init(void);
int uart_putc(char c, FILE * stream);
void uart_puts(const char * s);
char uart_getc(void);
void delay(int delay);
 
#endif /* UART_H_ */
</syntaxhighlight></tab>
<tab name="uart.c"><syntaxhighlight lang="c++" style="background: LightYellow;">
#include <avr/io.h>
 
#include <util/delay.h>
 
#include "uart.h"
 
void uart_init(void) {
    #include <util/setbaud.h>
 
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
    #if USE_2X
    UCSR0A |= (1 << U2X0);
    #else
    UCSR0A &= ~(1 << U2X0);
    #endif
 
    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
     UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}
 
int uart_putc(char c, FILE * stream) {
    if (c == '\n') {
        loop_until_bit_is_set(UCSR0A, UDRE0);
        UDR0 = '\r';
     }
     }
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;
    return 0;
}
}
</syntaxhighlight></tab>
 
void uart_puts(const char * s) {}
 
char uart_getc(void) {
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;
}
 
void delay(int delay) {
    for (int i = 1; i <= delay; i++)
        _delay_ms(1);
}
</syntaxhighlight></tab>  
</tabs>
</tabs>
Zdrojový kód: [[Médiá:MykytaOleksandr_sources.zip|MykytaOleksandr_sources.zip]]


=== Overenie ===
=== Overenie ===


Funkcia systemu bola overena pomocou vypisu dat cez UART. Do serioveho portu sa posielaju dvojice hodnot: x, y_sys. Tieto hodnoty je mozne zobrazit napriklad pomocou Serial Plotteru, kde je viditelny vstupny sinusovy signal a vystup systemu.
Funkcia systému bola overená pomocou výpisu dát cez UART. Do sériového portu sa posielajú dvojice hodnôt:
 
<code>x, y_sys</code>
 
Tieto hodnoty je možné zobraziť napríklad pomocou '''SerialPlot''', kde je viditeľný vstupný sinusový signal a výstup systému.
 
 
'''Namerené priebehy vstupu a výstupu zo Serial Plot'''
 
<gallery widths="200" heights="150" mode="packed">
Súbor:x_high.png|<code>x_max = 227</code>
Súbor:x_low.png|<code>x_min = 28</code>
Súbor:y_high.png|<code>y_max = 198</code>
Súbor:y_low.png|<code>y_min = 57</code>
</gallery>


Zo signalov je mozne pozorovat zmenu amplitudy a fazovy posun, co predstavuje bod frekvencnej charakteristiky systemu. System sa chova presne tak, ako hovori teoria.
Pri overovaní bolo sledované, či:
 
* amplitúda výstupu zodpovedá očakávaniu
* systém vykazuje fázový posun
* priebeh signalu je stabilný bez driftu
* výstup reaguje správne na vstupný sinus
 
Zo signalov je možné pozorovať zmenu amplitúdy a fázový posun, čo predstavuje bod frekvenčnej charakteristiky systému.
 
 
'''Video:'''
 
<center><youtube>MkgRE0QNgtk</youtube></center>
 
Video demonštruje reálne správanie systému. Je na ňom viditeľné generovanie sinusového signalu, jeho spracovanie systémom a výstupné dáta zobrazené v '''SerialPlot'''. Video slúži na overenie, že implementácia funguje podľa očakávania.


== Čo by som urobil inak ==
== Čo by som urobil inak ==


Pri rieseni by bolo mozne pouzit presnejsiu metodu diskretizacie systemu 1/(sT + 1), napriklad bilinearnu transformaciu, ktora by zlepsila presnost modelu.
Na ďalšíkrát by som si lepšie rozmyslel, ako presne riešiť diskretizáciu, lebo ten jednoduchý prístup funguje, ale nie je úplne ideálny.
 
Taktiez by bolo mozne implementovat presnejsi vypocet cos(θ) bez aproximacie, napriklad pomocou lookup tabulky.


Dalsim zlepsenim by mohlo byt automaticke vyhodnotenie amplitudy a fazoveho posunu priamo v mikrokontroleri namiesto spracovania na PC.
A ešte by som si skôr urobil priamo meranie aj na mikrokontroléri v kode, aby som nemusel všetko riešiť cez SerialPlot.


[[Category:AVR]] [[Category:MIPS]]
[[Category:AVR]] [[Category:MIPS]]

Aktuálna revízia z 18:51, 3. jún 2026

Záverečný projekt predmetu MIPS / LS2026 - Oleksandr Mykyta

Zadanie

Na generovanie harmonického signálu nepoužite funkcie sin(), resp. cos(), ale vytvorte oscilátor (v reálnom čase) ako prenosovú funkciu 1/((s*T)^2 + 1). Úlohou je zmerať jeden bod frekvenčnej charakteristiky systému 1/(s*T_osc+1) na frekvencii omega = 1/T. T = 0,5 sek. Výstupom je signál s parametrami: A_0 + A_1*sin(omega*t + fi), kde A_0 = 128 a A_1 = 100.


Úlohou bolo generovať harmonický signal bez použitia funkcií sin() alebo cos(). Na tento účel bol použitý oscilátor realizovaný ako prenosová funkcia:

H(s) = 1 / ((s · T)^2 + 1)

Zároveň bolo potrebné zmerať jeden bod frekvenčnej charakteristiky systému:

H(s) = 1 / (s · T + 1)

pre frekvenciu: ω = 1 / T, kde T = 0.5 s.

Výstupný signal má mať tvar:

A₀ + A₁ · sin(ωt + φ)

kde: A₀ = 128, A₁ = 100.


Literatúra:

Analýza a opis riešenia

Cieľom riešenia je vytvoriť sinusový signal bez použitia matematických funkcií sin() alebo cos(). Tento problém sa rieši pomocou diskretneho oscilátora, ktorý vychádza z diferenciálnej rovnice harmonického kmitania.

Základom je rovnica:

y + ω²y = 0

Táto rovnica popisuje harmonické kmity a jej riešením sú funkcie sin() a cos(). To znamená, že ak vieme túto rovnicu numericky riešiť, vieme generovať sinus.

Teoretický základ a odvodenia

Laplaceova transformacia

Vychadzajme z prenosovej funkcie systému:

H(s) = Y(s) / X(s) = 1 / (T · s + 1)

Po úprave:

Y(s) · (T · s + 1) = X(s)

T · s · Y(s) + Y(s) = X(s)

Použitím inverznej Laplaceovej transformacie (kde s predstavuje deriváciu) dostaneme:

T · dy(t)/dt + y(t) = x(t)

Týmto získame diferenciálnu rovnicu systému v časovej oblasti.

Diskretizacia

Mikrokontrolér pracuje v diskretnom čase, preto je potrebné nahradiť derivácie rozdielmi medzi vzorkami.

Pre druhú deriváciu použijeme aproximáciu:

y ≈ (y[n] − 2y[n−1] + y[n−2]) / T_s²

Po dosadení do y + ω²y = 0:

y[n] = (2 / (1 + ω²T_s²)) · y[n−1] − (1 / (1 + ω²T_s²)) · y[n−2]

Táto rovnica predstavuje numerickú aproximáciu oscilátora, ale nie je ideálna z hľadiska stability amplitúdy.

Diskretny oscilátor (presný model)

Presnejší prístup vychádza z trigonometrických identít:

sin(A + B) = sin(A)cos(B) + cos(A)sin(B)

sin(A − B) = sin(A)cos(B) − cos(A)sin(B)

Po sčítaní:

sin(A + B) + sin(A − B) = 2 · sin(A) · cos(B)

Dosadením:

A = (n−1)θ, B = θ

dostaneme:

sin(nθ) + sin((n−2)θ) = 2 · cos(θ) · sin((n−1)θ)

Označením:

y[n] = sin(nθ)

vznikne rekurentný vzťah:

y[n] = 2 · cos(θ) · y[n−1] − y[n−2]

Tento vzťah generuje stabilný sinusový signal bez zmeny amplitúdy.

Výpočet parametrov

Platí:

θ = ω · T_s

kde: T = 0.5 s ω = 1 / T = 2 rad/s SAMPLE_RATE = 1000 Hz T_s = 0.001 s


θ = 0.002

Aproximacia cos()

Keďže nie je dovolené použiť funkciu cos(), použije sa Taylorov rozvoj:

cos(θ) ≈ 1 − θ²/2

Z toho:

2cos(θ) ≈ 2 · (1 − θ²/2)

Inicializácia oscilátora

Pre správnu činnosť oscilátora sú potrebné počiatočné hodnoty:

y1 = 1, y2 = 1 − θ²/2

Generovanie signalu

Oscilátor generuje hodnoty v rozsahu:

[-1, 1]

Požadovaný výstup:

x = A0 + A1 · y

kde:

A0 = 128 A1 = 100

Diskretizacia systému 1 / (sT + 1)

Zo spojitej rovnice:

T · dy/dt + y = x

Použitím Eulerovej metódy:

dy/dt ≈ (y[n] − y[n−1]) / T_s

Po úprave:

y[n] = x − (T / T_s) · y[n] − (T / T_s) · y[n−1]

y[n] · (1 + (T / T_s)) = x − (T / T_s) · y[n−1]

y[n] = (T_s / (T + T_s)) · x + (T / (T + T_s)) · y[n−1]

Po zavedení:

α = T_s / (T + T_s)

dostaneme praktický tvar:

y[n] = α · x + (1 − α) · y[n−1]

y[n] = y[n−1] + α · (x − y[n−1])

Algoritmus a program

Algoritmus programu využíva diskretny oscilátor a numericku aproximáciu systému 1 / (sT + 1). Základné výpočty prebiehajú v prerušení Timer1 s frekvenciou 1 kHz:

  • výpočet oscilátora
  • generovanie vstupu x
  • výpočet výstupu systému
  • výstup cez PWM
  • odosielanie dát cez UART
#define F_CPU 16000000 UL

#include <avr/io.h>

#include <avr/interrupt.h>

#include <stdio.h>

#include "uart.h"

#define SAMPLE_RATE 1000.0
#define T 0.5

#define A0 128
#define A1 100

float OSC_COEFF;

volatile float y = 0;
volatile float y1 = 0;
volatile float y2 = 0;

volatile float y_sys = 0;

float alpha;

FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);

ISR(TIMER1_COMPA_vect) {
    y = OSC_COEFF * y1 - y2;

    y2 = y1;
    y1 = y;

    float x = A0 + A1 * y;

    y_sys = y_sys + alpha * (x - y_sys);

    OCR0A = (uint8_t)(y_sys);

    printf("%d,%d\n", (int) x, (int) y_sys);

}

void timer1_init() {
    TCCR1B |= (1 << WGM12);

    OCR1A = 15999;

    TCCR1B |= (1 << CS10);

    TIMSK1 |= (1 << OCIE1A);

}

void pwm_init() {
    DDRD |= (1 << PD6);

    TCCR0A |= (1 << COM0A1) | (1 << WGM01) | (1 << WGM00);
    TCCR0B |= (1 << CS01);

}

int main(void) {
    uart_init();
    stdout = & mystdout;

    pwm_init();
    timer1_init();

    float Ts = 1.0 / SAMPLE_RATE;

    alpha = Ts / (T + Ts);

    float theta = (1.0 / T) * (1.0 / SAMPLE_RATE);

    y1 = 1.0;
    y2 = 1.0 - (theta * theta) / 2.0;

    OSC_COEFF = 2.0 * (1.0 - (theta * theta) / 2.0);

    sei();

    while (1) {}

}
#define set_bit(ADDRESS, BIT)(ADDRESS |= (1 << BIT))
#define clear_bit(ADDRESS, BIT)(ADDRESS &= ~(1 << BIT))

#ifndef UART_H_
#define UART_H_

#include <stdio.h>

#define BAUD_PRESCALE(((F_CPU / (BAUDRATE * 16 UL))) - 1)

void uart_init(void);
int uart_putc(char c, FILE * stream);
void uart_puts(const char * s);
char uart_getc(void);
void delay(int delay);

#endif /* UART_H_ */
#include <avr/io.h>

#include <util/delay.h>

#include "uart.h"

void uart_init(void) {
    #include <util/setbaud.h>

    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
    #if USE_2X
    UCSR0A |= (1 << U2X0);
    #else
    UCSR0A &= ~(1 << U2X0);
    #endif

    UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}

int uart_putc(char c, FILE * stream) {
    if (c == '\n') {
        loop_until_bit_is_set(UCSR0A, UDRE0);
        UDR0 = '\r';
    }

    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = c;

    return 0;
}

void uart_puts(const char * s) {}

char uart_getc(void) {
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;
}

void delay(int delay) {
    for (int i = 1; i <= delay; i++)
        _delay_ms(1);
}

Zdrojový kód: MykytaOleksandr_sources.zip

Overenie

Funkcia systému bola overená pomocou výpisu dát cez UART. Do sériového portu sa posielajú dvojice hodnôt:

x, y_sys

Tieto hodnoty je možné zobraziť napríklad pomocou SerialPlot, kde je viditeľný vstupný sinusový signal a výstup systému.


Namerené priebehy vstupu a výstupu zo Serial Plot

Pri overovaní bolo sledované, či:

  • amplitúda výstupu zodpovedá očakávaniu
  • systém vykazuje fázový posun
  • priebeh signalu je stabilný bez driftu
  • výstup reaguje správne na vstupný sinus

Zo signalov je možné pozorovať zmenu amplitúdy a fázový posun, čo predstavuje bod frekvenčnej charakteristiky systému.


Video:

Video demonštruje reálne správanie systému. Je na ňom viditeľné generovanie sinusového signalu, jeho spracovanie systémom a výstupné dáta zobrazené v SerialPlot. Video slúži na overenie, že implementácia funguje podľa očakávania.

Čo by som urobil inak

Na ďalšíkrát by som si lepšie rozmyslel, ako presne riešiť diskretizáciu, lebo ten jednoduchý prístup funguje, ale nie je úplne ideálny.

A ešte by som si skôr urobil priamo meranie aj na mikrokontroléri v kode, aby som nemusel všetko riešiť cez SerialPlot.