Integrátor s mikroprocesorom
Zo stránky SensorWiki
Záverečný projekt predmetu MIPS / LS2026 - Dmytro Domchuk
Zadanie
Integrátor. Potenciometer zadá veľkosť vstupu integrátora. Vytvorí sa zdroj reálneho času, prírastku času, napr. 5ms. A s nastavenou integračnou časovou konštantou sa bude meniť v reálnom čase, výstup, ktorý sa bude zobrazovať na serialplot. Takže, sčitame hodnotu ADC vstupu z potenciometra a budeme ju integrovať každych 5ms. Ďalej, vysledky budeme posielať cez seriovu linku do programu SerialPlot.

Literatúra:
Analýza a opis riešenia
Našou ulohou bolo vytvoriť integrator na zaklade vyvijivej dosky Acrob a petenciometroveho modula. Potenciometrový model sa saklada z potenciometra, prepinača režimu fungovania(linearný alebo parabolický), reset tlačidko a kontrolera. Zapojili sme modul potenciometra ku doske Acrob na port X4. A pripojili Acrob ku počitaču pomocou USB kabla. Na moldule potenciometra nastavujeme velkosť potenciometra, sčitavame hodnotu z ADC, vypočitame integral a posielame dve hodnoty na SerialPlot. Prva hodnota je velkosťou vstupu a druha je integrolom.

Nezabudnite doplniť schému zapojenia! V texte by ste mali opísať základné veci zo zapojenia, samotná schéma nie je dostačujúci opis.

Algoritmus a program
Algoritmus programu v hlavnej slučke sa začina tym že robí inicializaciu UART, ADC a Timer1 apovoluje prerušenia pomocou sei(). Ďalej zapisuje parametre integratora. Potom program zčita hodnotu z ADC a spravi posun nuly. Ďalej sa vytvori pasmo necitlivosti na +-3 jednotky z hodnoty ADC. Potom program vypočita integral Eulerovou metodou a spravi anti-windup aby hodnota integrala ne prekročila hranice 10000 a -10000. Ďalej v programe vytvorime 2 premennych typu int16_t plot_input a plot_ integral ktore sa rovnaju hodnotam input a integral. Ďalej posielame hodnoty premennych cez seriovu linku v binarnom tvare. Funkcia void UART_init() robo inicializaciu UART pre baud 14400 a posialanie 8 datovych bitov a 1 stop bit. Funkcia void UART_transmit_byte(uint8_t data) čaka pokial bufer sa vyprazdni a posiela 8 datovych bitov. Funkcia void UART_send_int16(int16_t value) 2 krat použiva predchadzajucu funkciu na posielani jedneho čisla typu int16. Inicializaciu ADC robi void ADC_init(). Funkcia uint16_t ADC_read() načitava hodnotu z ADC.Inicializujeme Timer1 v void Timer1_init(). Funkcia ISR(TIMER1_COMPA_vect) je rutiniu prerušenia. Ne použil som funkcie uart.c a adc.c zo semestra lebo musel by som ich moc silno zmeniť, takže na jeden krat stači aj realizacia priamo v programe. Ale pri vetšiem počte podobnych programov alebo pre zabezpečenie fungovania na viacerych zariadeniach radšej spraviť zvlašť hlavnu slučku a funkcie.
#include <avr/io.h>
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
// Globálne premenné
volatile uint8_t timer_flag = 0;// Príznak z prerušenia
// --- Inicializácia UART pre 14400 baud (pri 16 MHz) ---
void UART_init() {
uint16_t ubrr = 68;
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)ubrr;
UCSR0A &= ~(1 << U2X0); // Dvojnásobná rýchlost off
UCSR0B = (1 << TXEN0); // Povolenie vysielaca
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
} // 8 dátových bitov, 1 stop bit . Odošle jeden znak
void UART_transmit_byte(uint8_t data) {
while (!(UCSR0A & (1 << UDRE0))); // caká na vyprázdnenie buffra
UDR0 = data;
}
void UART_send_int16(int16_t value){
UART_transmit_byte((uint8_t)(value & 0xFF));
UART_transmit_byte((uint8_t)((value>>8) & 0xFF));
}
// --- Inicializácia ADC ---
void ADC_init() { // Referencia AVCC (REFS0=1), výber kanála ADC0 (MUX = 0000)
ADMUX = (1 << REFS0)|(1<<MUX2); // Povolenie ADC (ADEN=1), preddelic 128 (ADPS2..0 = 111) pre frekvenciu 125 kHz
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); }
// Nacítanie hodnoty z ADC0
uint16_t ADC_read() {
ADCSRA |= (1 << ADSC); // Spustenie prevodu
while (ADCSRA & (1 << ADSC)); // cakanie na dokoncenie
return ADC;
}
// --- Inicializácia Timer1 pre 5ms prerušenie ---
void Timer1_init() {
// Režim CTC (Clear Timer on Compare Match),
TCCR1B |= (1 << WGM12);
// Nastavenie preddelica na 64
// Frekvencia casovaca = 16 MHz / 64 = 250 kHz
// Požadovaný cas = 5 ms (0.005 s)
// Hodnota do OCR1A = (0.005 * 250000) - 1 = 1249
OCR1A = 1249;
// Povolenie prerušenia pri zhode (Compare Match A)
TIMSK1 |= (1 << OCIE1A);
// Spustenie casovaca s preddelicom 64 (CS11=1, CS10=1)
TCCR1B |= (1 << CS11) | (1 << CS10); }
// Rutina obsluhy prerušenia od Timer1 (vykoná sa každých 5 ms)
ISR(TIMER1_COMPA_vect) {
timer_flag = 1; // Len nastavíme vlajku, výpocet urobíme v main slu?ke
}
int main(void) {
UART_init();
ADC_init();
Timer1_init();
sei(); // Globálne povolenie prerušení
// Parametre integrátora
const float Ts = 0.005; // 5 ms
const float Ti = 1.0;
float integral = 0.0;
float input =0;
while (1) {
if (timer_flag) {
timer_flag = 0; // Zhodenie vlajky
// 1. Nacítanie vstupu a posunutie nuly
uint16_t raw_adc = ADC_read();
input = (float)(raw_adc - 512);
// Softvérové pásmo necitlivosti (Deadband)
// Ignoruje jemný šum (napr. +- 3 hodnoty z ADC okolo stredu), aby integrátor nedriftoval
if (input > -3.0 && input < 3.0) {
input = 0.0;
}
// 2. Výpocet (Eulerova dopredná metóda)
integral = integral + (Ts / Ti) * input;
// Anti-windup
if (integral > 10000.0) integral = 10000.0;
if (integral < -10000.0) integral = -10000.0;
int16_t plot_input=(int16_t)input;
int16_t plot_integral=(int16_t)integral;
// 4. Odoslanie vo formáte pre Serial Plotter: "Hodnota1,Hodnota2\n"
UART_send_int16(plot_input);
UART_send_int16(plot_integral);
UART_transmit_byte('\r');
UART_transmit_byte('\n');
}
}
}
#include <avr/io.h>
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
void UART_init();
void UART_transmit_byte(uint8_t data);
void UART_send_int16(int16_t value);
void ADC_init();
uint16_t ADC_read();
void Timer1_init();
ISR(TIMER1_COMPA_vect);
int main(void);
Zdrojový kód: zdrojak Domchuk.zip
Overenie
Overovali sme naš program tym že menili sme polohu potenciometra a pozerali ako sa zmeni graf na serial plot.








Čo by som urobil inak
Nabuduce pre riešenie tohto problemu zrušil by som posun nuly pri sčitanií ADC. Takže zmenil by som Baud na dajme tomu 115200 pre rychlejší prenos signalu. Takže nabuduce spravil by som to z viac profesionalnym pristupom a vytvorenim samostatnych funkcí. Nepodarilo sa mi odladiť program tak aby signal bol ustalený a ne kmital.