Operácie

Integrátor s mikroprocesorom

Zo stránky SensorWiki

Verzia z 12:54, 24. jún 2026, ktorú vytvoril Balogh (diskusia | príspevky) (→‎Algoritmus a program)
(rozdiel) ← Staršia verzia | Aktuálna úprava (rozdiel) | Novšia verzia → (rozdiel)

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.

Vývojová doska ACROB.

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.

Celkový pohľad na zariadenie.

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.

Schéma zapojenia.


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.

Poloha potenciometra 0.
Graf pri polohe potenciometra 0.
Poloha potenciometra 3.7.
Graf pri polohe potenciometra 3.7.
Poloha potenciometra 4.2.
Graf pri polohe potenciometra 4.2.
Poloha potenciometra 5.2.
Graf pri polohe potenciometra 5.2.

Č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.