Generovanie kódu z prostredia Matlaba/Simulink: Rozdiel medzi revíziami
Zo stránky SensorWiki
Riadok 6: | Riadok 6: | ||
Na platforme Arduino Uno R3 (ATmega328P) implementovať diskrétny filter prvého rádu so spojitou prenosovou funkciou G(s)=K/(T*s+1), K=1[-], T=0,5 [s], so vzorkovacím časom Ts=0,01. Vstupom je jednorazový skok napätia z 0 V na 2,00 V (potenciometer na A0). Výstup má byť v celočíselnej aritmetike (integer), formátovaný ako text "x.yy\n" a odoslaný cez UART0 (115200 Bd). Grafické zobrazenie prechodovej charakteristiky sa vykoná v SerialPlot. | Na platforme Arduino Uno R3 (ATmega328P) implementovať diskrétny filter prvého rádu so spojitou prenosovou funkciou G(s)=K/(T*s+1), K=1[-], T=0,5 [s], so vzorkovacím časom Ts=0,01. Vstupom je jednorazový skok napätia z 0 V na 2,00 V (potenciometer na A0). Výstup má byť v celočíselnej aritmetike (integer), formátovaný ako text "x.yy\n" a odoslaný cez UART0 (115200 Bd). Grafické zobrazenie prechodovej charakteristiky sa vykoná v SerialPlot. | ||
[[Obrázok: | [[Obrázok:arduino.jpg|400px|thumb|center|Vývojová doska ACROB.]] | ||
'''Literatúra:''' | '''Literatúra:''' |
Verzia z 17:03, 12. jún 2025
Záverečný projekt predmetu MIPS / LS2025 - Meno Priezvisko
Zadanie
Na platforme Arduino Uno R3 (ATmega328P) implementovať diskrétny filter prvého rádu so spojitou prenosovou funkciou G(s)=K/(T*s+1), K=1[-], T=0,5 [s], so vzorkovacím časom Ts=0,01. Vstupom je jednorazový skok napätia z 0 V na 2,00 V (potenciometer na A0). Výstup má byť v celočíselnej aritmetike (integer), formátovaný ako text "x.yy\n" a odoslaný cez UART0 (115200 Bd). Grafické zobrazenie prechodovej charakteristiky sa vykoná v SerialPlot.

Literatúra:
Analýza a opis riešenia
V tejto časti popíšeme, prečo a ako sme diskrétny filter navrhli, od spojitého modelu až po finálnu integer implementáciu.
1. Spojitý model
• Prenosová funkcia prvého rádu je G(s)=1/(0,5*s+1), čo znamená, že pri skoku vstupu sa výstup exponenciálne približuje k cieľovej hodnote s časovou konštantou T = 0,5 s.
2. Prečo digitálny filter
• Mikrokontrolér ATmega328P pracuje v diskrétnom čase a vyžaduje digitálne spracovanie signálu.
• ADC prevádza analógový signál na čísla, následný filter sa implementuje v kóde ako rozdielová rovnica.
3. Diskretizácia (ZOH)
• Pri vzorkovacom čase Ts = 0,01 [s]: a = exp(–Ts/T) = exp(–0,01/0,5) ≈ 0,98; b = 1 – a ≈ 0,02.
• Rovnica v diskrétnej podobe: y[n] = 0,98*y[n–1] + 0,02*u[n].
4. Integer aproximácia
• Pre rýchle a efektívne výpočty na AVR používame celočíselný zápis: K1 = 98, K2 = 2, deliteľ = 100.
• Riešenie: y[n] = (98*y[n–1] + 2*u[n]) / 100.
5. Mapovanie ADC signálu
• ADC hodnota 0–1023 zodpovedá 0–5 V.
• Pre zobrazenie v stotinách voltu: volt = (adc_val * 500) / 1023, výsledok 0–500.
6. Model v MATLAB/Simulink
Nižšie je zobrazená schéma, ktorú sme vytvorili v Simulinku na simuláciu diskrétneho filtra (vstup , výstup ):

Vo vnútri bloku simulink sa nachádza MATLAB Function s implementáciou v integer aritmetike:
function y = simulink(u)
%#codegen
persistent y_prev
if isempty(y_prev)
y_prev = int32(0);
end
% y[n] = (98*y_prev + 2*u) / 100
y = (98*y_prev + 2*int32(u)) / 100;
y_prev = y;
end
Tento model sme generátorom kódu premenili na C-knihovne simulink.c a simulink.h.
Algoritmus a program
1. Inicializácia periférií
• init_uart() (uart.c): Nastaví UART0 na 115200 Bd.
• init_adc() (p_f_1.c): ADC0, AVcc referencia, prescaler 128, povolené ADC prerušenie.
• init_timer2() (p_f_1.c): Timer2 CTC, prescaler 1024, OCR2A=156 → prerušenie každých 10 ms.
• sei() (main.c): povolenie globálnych prerušení.
• simulink_init() (simulink.c): vynulovanie vnútorného stavu filtra (y_prev=0)
2. Obsluha prerušení
• Timer2_COMPA_vect (p_f_1.c)
• ADC_vect (p_f_1.c)
3. Hlavná slučka (main.c)
Hlavná slučka beží v nekonečnej slučke while(1) a čaká na príznak new_sample, ktorý sa nastaví v ADC ISR
// simulink.c
#include "simulink.h"
static int32_t y_prev = 0;
// Reset vnútorného stavu filtra
void simulink_init(void)
{
y_prev = 0;
}
// Výpočet nového výstupu filtra
int32_t simulink_step(int32_t u)
{
y_prev = (98 * y_prev + 2 * u) / 100;
return y_prev;
}
// simulink.h
#ifndef SIMULINK_H
#define SIMULINK_H
#include <stdint.h>
// Resetuje vnútorný stav filtra (y[n-1]=0)
void simulink_init(void);
// Vypočíta y[n] = (98·y[n-1] + 2·u) / 100
int32_t simulink_step(int32_t u);
#endif // SIMULINK_H
// p_f_1.c
#include "p_f_1.h"
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint16_t adc_val = 0;
volatile uint8_t new_sample = 0;
// Inicializácia ADC0 + povolenie ADC ISR
void init_adc(void)
{
ADMUX = (1<<REFS0); // AVcc, ADC0
ADCSRA = (1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
// EN, IE, prescaler=128 → ~125 kHz ADC clock
}
// Konfigurácia Timer2 CTC pre 10ms
void init_timer2(void)
{
TCCR2A = (1<<WGM21); // CTC režim
TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // prescaler=1024
OCR2A = 156; // 16 MHz/1024/156 ≈ 100 Hz
TIMSK2 = (1<<OCIE2A); // Compare Match A interrupt
}
// ADC konverzia
ISR(TIMER2_COMPA_vect)
{
ADCSRA |= (1<<ADSC); // spustí ADC konverziu
}
//Uloženie adc_val a nastavenie new_sample
ISR(ADC_vect)
{
adc_val = ADC; // zachytí ADC výsledok
new_sample = 1; // príznak pre hlavný loop
}
// p_f_1.h
#ifndef P_F_1_H
#define P_F_1_H
#include <stdint.h>
// ISR sem uloží 10-bit ADC hodnotu (0..1023)
extern volatile uint16_t adc_val;
// ISR sem nastaví príznak novej vzorky každých ~10 ms
extern volatile uint8_t new_sample;
// Inicializuje ADC0 (AVcc ref, prescaler=128, interrupt enable)
void init_adc(void);
// Inicializuje Timer2 CTC (prescaler=1024, OCR2A=156 → ~10 ms) + interrupt
void init_timer2(void);
#endif // P_F_1_H
// uart.c
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#ifndef BAUD
#define BAUD 115200UL
#endif
#include <avr/io.h>
#include <util/setbaud.h>
#include "uart.h"
// Konfigurácia UART0
void init_uart(void)
{
// nakonfiguruje UBRR0 podľa makier a setbaud.h
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1<<U2X0);
#else
UCSR0A &= ~(1<<U2X0);
#endif
// 8 dátových bitov, žiadna parita, 1 stop bit
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
// povol TX
UCSR0B = (1<<TXEN0);
}
// Posielanie C-reťazca cez UART0
void uart_puts(const char *s)
{
while (*s) {
if (*s == '\n') {
// vlož carriage return pred newline
while (!(UCSR0A & (1<<UDRE0)));
UDR0 = '\r';
}
while (!(UCSR0A & (1<<UDRE0)));
UDR0 = *s++;
}
}
// uart.h
#ifndef UART_H
#define UART_H
// Inicializuje UART0 na 8N1 s BAUD (máme ho definované v main.c)
void init_uart(void);
// Pošle C-string, vloží '\r' pred každé '\n'
void uart_puts(const char *s);
#endif // UART_H
// main.c
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#ifndef BAUD
#define BAUD 115200UL
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h> // pre snprintf
#include "p_f_1.h" // init_adc, init_timer2, extern adc_val, new_sample
#include "uart.h" // init_uart, uart_puts
#include "simulink.h" // simulink_init, simulink_step
int main(void)
{
char buf[16];
// 1) Inicializácia periférií
init_uart(); // UART0 na 115200
init_adc(); // ADC0 s prerušeniami
init_timer2(); // Timer2 CTC ~10 ms
sei(); // povoli globálne prerušenia
// 2) Reset stavu filtra
simulink_init();
// 3) Hlavná slučka
while (1)
{
if (new_sample)
{
new_sample = 0; //Reset príznaku
// Prevzorkovanie ADC 0..1023 → 0..500 (stotiny voltu)
uint16_t volt = (adc_val * 500UL) / 1023;
// Výpočet filtra 1. rádu
int32_t filt = simulink_step((int32_t)volt);
// Formátovanie výstupu na ASCII "x.yy\n" pre SerialPlot
int whole = filt / 100;
int frac = filt % 100;
snprintf(buf, sizeof(buf), "%d.%02d\n", whole, frac);
uart_puts(buf); // Odoslanie cez UART
}
}
return 0;
}
Pridajte sem aj zbalený kompletný projekt, napríklad takto (použite jednoznačné pomenovanie, nemôžeme mať na serveri 10x zdrojaky.zip:
Zdrojový kód: zdrojaky.zip
Zapojenie
Pripojenie potenciometra (B10K) na Arduino UNO kvôli možnosti plynulej zmeny vstupného napätia v rozsahu 0-5V.

Overenie
1. Arduino IDE Serial Monitor (115200 Bd): overené prijímanie hodnôt "0.00", "2.00" po skoku.
2. SerialPlot (ASCII, prázdny delimiter, 100 Hz): zobrazená exponenciálna prechodová charakteristika z 0 V na 2,00 V.
• Inicializácia: Načítali sme spúšťací skok do 2,00 V.
• Spustenie: Otáčaním potenciometra z 0 V na 2,00 V sme generovali jednorazový skok.
• Záznam dát: SerialPlot zaznamenával postupnosť prijímaných hodnôt, Arduino do PC posielalo reťazce „x.yy “ každých 10 ms.
• Opakované merania: Krátke kroky sme zopakovali viackrát, aby sme overili opakovateľnosť.

Video:
Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.