Operácie

Riadenie sústavy 1. rádu: Rozdiel medzi revíziami

Z SensorWiki

Riadok 70: Riadok 70:
  
 
<tabs>
 
<tabs>
 +
 
<tab name="Cv_Mer_prech_ch_12.c"><source lang="c++" style="background: LightYellow;">
 
<tab name="Cv_Mer_prech_ch_12.c"><source lang="c++" style="background: LightYellow;">
 +
 
/*
 
/*
 
  * Mer_prech_charak.c
 
  * Mer_prech_charak.c
Riadok 174: Riadok 176:
 
</source></tab>
 
</source></tab>
 
<tab name="p_f_1.c"><source lang="c++" style="background: LightYellow;">
 
<tab name="p_f_1.c"><source lang="c++" style="background: LightYellow;">
 +
 
/*
 
/*
 
  * p_f_1.c
 
  * p_f_1.c
Riadok 294: Riadok 297:
 
zob_text_UART(Riadok);
 
zob_text_UART(Riadok);
 
}
 
}
 +
  
 
</source></tab>
 
</source></tab>
 
<tab name="p_f_1.h"><source lang="c++" style="background: LightYellow;">
 
<tab name="p_f_1.h"><source lang="c++" style="background: LightYellow;">
 +
 
/*
 
/*
 
  * p_f_1.h
 
  * p_f_1.h
Riadok 362: Riadok 367:
  
 
#endif /* P_F_1_H_ */
 
#endif /* P_F_1_H_ */
 +
 +
 
</source></tab>
 
</source></tab>
 
<tab name="Arduino code"><source lang="arduino" style="background: #9dd1e1;">
 
<tab name="Arduino code"><source lang="arduino" style="background: #9dd1e1;">
  
 
/* Arduino code az niekedy inokedy */
 
/* Arduino code az niekedy inokedy */
 +
  
 
</source></tab>
 
</source></tab>
Riadok 405: Riadok 413:
 
''Výstup zo “SerialPtot“ pre P regulátor so zosilnením 5 by mohol vyzerať takto.''
 
''Výstup zo “SerialPtot“ pre P regulátor so zosilnením 5 by mohol vyzerať takto.''
 
</div>
 
</div>
 +
  
 
<tabs>
 
<tabs>
 +
 
<tab name="Cv_P_reg_12.c"><source lang="c++" style="background: LightYellow;">
 
<tab name="Cv_P_reg_12.c"><source lang="c++" style="background: LightYellow;">
 
/*
 
/*
Riadok 502: Riadok 512:
 
  }  
 
  }  
 
}
 
}
 +
  
  
 
</source></tab>
 
</source></tab>
 
<tab name="p_f_1P.h"><source lang="c++" style="background: LightYellow;">
 
<tab name="p_f_1P.h"><source lang="c++" style="background: LightYellow;">
 +
 +
 
/*
 
/*
 
  * p_f_1.h
 
  * p_f_1.h
Riadok 572: Riadok 585:
  
 
#endif /* P_F_1_H_ */
 
#endif /* P_F_1_H_ */
 +
 +
 
</source></tab>
 
</source></tab>
 
<tab name="p_f_1P.c"><source lang="c++" style="background: LightYellow;">
 
<tab name="p_f_1P.c"><source lang="c++" style="background: LightYellow;">
 +
 +
 
/*
 
/*
 
  * p_f_1_P_reg.c
 
  * p_f_1_P_reg.c
Riadok 697: Riadok 714:
 
OCR0B  = u_reg ;
 
OCR0B  = u_reg ;
 
}
 
}
 +
 +
  
 
</source></tab>
 
</source></tab>
Riadok 702: Riadok 721:
  
 
/* Arduino code az niekedy inokedy */
 
/* Arduino code az niekedy inokedy */
 +
  
 
</source></tab>
 
</source></tab>
 +
 
</tabs>
 
</tabs>
  

Verzia zo dňa a času 16:49, 5. máj 2021


Azda najjednoduchší dynamický elektrický obvod je tvorený odporom a kondenzátorom. Takýto RC člen má výrazné filtračné účinky, používa sa napríklad na vyhladenie PWM priebehov, filtráciu vysokofrekvenčných zložiek signálu, ochranu kontaktov relé a pod. Veľa krát je vo funkcii filtra PWM signálu, je regulovaná sústava. To je náš prípad.

RC-modul.jpgRC-schema1.png
Komerčný modul na ochranu kontaktov relé a jeho schéma zapojenia (RC obvod).

Schému zapojenia doplníme o označenie vstupných a výstupných signálov. . Bude nás zaujímať priebeh napätia na kondenzátore po pripojení napätia 5V. Je zrejmé, že kondenzátor sa bude nabíjať a že rýchlosť nabíjania bude závisieť od veľkosti rezistora R. Čim vyššia bude hodnota odporu, tým menší bude nabíjací prúd a tým dlhšie sa bude kondenzátor nabíjať. Podobná situácia bude platiť aj pre vybíjanie. Ale aký bude presný tvar nabíjacej krivky? Nabíjacia krivka je vlastne prechodová charakteristika. Prepokladáme, že pripojený zdroj má prakticky nulový vnútorný odpor.

RC-schema2.png
Jednoduchý RC obvod s doplneným označením veličín.

V elektrickom obvode podľa obrázku podľa Kirchoffovho zákona platí, že súčet napätí v uzavretej slučke je nulový:


  u_{in}(t) - R i(t) - u_{out}(t) = 0,

kde i(t) je prúd v slučke a R je odpor rezistora, pričom pre výstupné napätie u_{out} ďalej platí


  u_{out}(t) = \frac{1}{C} \int_{0}^{t} i(t) dt,

kde C je kapacita kondenzátora.


Po zohľadnení počiatočných podmienok dostaneme riešením diferenciálnej rovnice pre skokovú zmenu vstupného napätia u_{in} z hodnoty 0 na U_0 v čase t=0 nasledovný priebeh výstupného napätia u_{out}:


   u_{out}(t) = U_{0} K (1-e^{-t/T})

kde K=1 je tzv. zosilnenie obvodu a T = RC je tzv. časová konštanta. Jej veľkosť určuje tvar a rýchlosť nabíjacej/vybíjacej krivky kondenzátora. Vybíjacia krivka predpokladá skrat vstupných svoriek. Nestačí len rozpojiť spínač.


RC-simulaciaT.png
Časové priebehy vstupného (modrá) a výstupného (červená) napätia - simulácia.


Ako z nameraného priebehu zistíme časovú konštantu? Zabudnite na poučky o dotyčnici v počiatočnom bode...

Pozrime sa, na akú hodnotu stihne vystúpiť napätie za čas rovný presne jednej časovej konštante. Ak do funkcie pre priebeh výstupného napätia dosadíme za čas t = T, dostaneme


   u_{out}(T) = U_{0} K (1-e^{-T/T})  = U_{0} K (1-e^{-1})  = 0,632 U_{0} K

teda na 63,2% z ustálenej hodnoty. To vieme vypočítať celkom presne, je to 0,632*5 = 3,16 V. Ak teda nájdenme na grafe hodnotu napätia 3,16 V, na časovej osi tomu zodpovedajúci čas je priamo hodnota T. Na obrázku vyššie je tento čas 2 sekundy, ale keďže skoková zmena na vstupe nastala v čase 1 sekunda, hodnota časovek konštanty je 2-1 = 1 sekunda.

No a presne to treba spraviť aj pomocou vášho mikropočítača. Aby sme merania vedeli priradiť k reálnemu času, potrebujeme merania robiť s nejakou presnou periódou vzorkovania. Na to nám poslúži počítadlo T2, ktoré nastavíme tak, aby vyvolalo požiadavku o výpis hodnoty napätia na kondenzátore každých 10 ms. Samotné počítadlo bude generovať prerušenie každé 2 ms. Toto prerušenie použijeme ako “generátor” 10ms vzoriek a na opakované spustenie AD prevodu. [1]. Teoretická hodnota časovej konštanty je 0,5 s. T.j. Presnosť merania postačuje. Napätie na kondenzátore je merané nasledovným spôsobom: AD prevodník je nastavený na maximálnú možnú rýchlosť realizácie AD prevodu. Prevod je spustený v prerušení od T2, t.j. každé 2ms. Je povolené prerušenie od ukončenia AD prevodu. V obsluhe prerušenia prečítame napätie na kondenzátore. Ak treba, môžeme ho aj, napr. Filtrom typu “kĺzavý priemer” filtrovať. Ak by sme merali viac AD kanálov nastavili by sme ďalší analógový vstup. Spustenie AD prevodu sa zase uskutoční v prerušení od T2 (každé 2 ms).

Vzorový program predpokladá pripojenie 5V (pin D5) na RC člen. V takomto prípade by sme mohli vykreslovať aj meraný priebeh napätia nav stupe RC člena. My ale budeme vstupné napätie generovať ako PWM signal (Fast PWM MOD3 výstup na pin D5). T.j. bude vhodnejšie vykreslovať teoretický priebeh strednej hodnoty PWM signálu. Na začiatku nastavíme plnenie na nulu, čo odpovedá napätiu 0V a po 1 s zmeníme na plnenie odpovedajúce hodnote napr. 4V (plnenie nastavíme do registra … ako číslo z intervalu (0 až 255)). Teoretický čas ustálenia napätia na kondenzátore je 5*T (časová konštanta RC člena - T=R*C) Po tomto čase výpis cez sériový kanál na SERIAL PLOTER ukončíme. Ak by sme chceli opakovanie vykreslovať prechodovú charakteristiku, musíme najskôr kondenzátor vybiť. To sad á napr. Tak, že pripojíme malý resistor, napr. 330Ohm cez pin D4. Ten na začiatku nastavíme ako výstupný na hodnotu LOW. V okamžiku začatia generovania PWM signálu pin D4 nastavíme ako vstupný a vybíjanie tým ukončíme. Keďže potrebujeme poznať čas ( násobok 10ms) budeme na základe info s T2 inkrementovať premenú, ktorá bude načítavať údaj o čase. Je vhodné do grafu okrem napätia na kondenzátore vykreslovať ustálenú hodnotu a hodnotu odpovedajúcu “časovej konštante”. Vykreslovať môžeme realizovať v rozsahu 0 až 5V, resp. v rozsahu AD prevodníka 0 až 1023, resp. v tzv. MU rozsah 0 až 1.


Úloha 1: Odmerajte časový priebeh signálov pri nabíjaní kondenzátora C cez rezistor R. Z nameraného grafu určte hodnotu časovej konštanty T a spresnite hodnoty R a C.


Zdrojovy kod pre AVRGCC a ARDUINO


/*
 * Mer_prech_charak.c
 * Meranie prechodovej charakteristiky
 * Použijeme SerialPlott-er
 --------------------------
 Program pre vykreslenie prechodovej charakteristiky RC clena
* Použite 47uF  kondezátor
* pinA (D5) je vždy zapojený cez 10kOhm rezistor ako OUTPUT. 
* pinB (D4) je najskôr zapojený ako output a následne nastavený do log. nuly. 
* pinB a A0 sú skratnuté.
* za cca 1sekundu (delay(1000ms)) po resete bude kondenzátor urcite vybitý. Potom sa
* pinB prepne do stavu input bez pullup a kondenzátor sa zacne nabíjat.
* Ked program dosiahne cas (6+1)*T (T - casová konštanta RC clena) vykreslovanie sa zastaví. 
*
* Vykreslovanie  na SerialPlott-er sa deje v diskrétnom case. Každých n*Tv (n = 0, 1, 2, ...), 
* kde Tv je perióda vzorkovania, tu 10 ms
* Vykreslovanie je v pomerných jednotkách. 1SJ = 1023 (10b-ový prevodník).
* 1SJ odpovedá napätiu 5V
* Vykreslujú sa tri ciary:
* 1.) y_c, 2.) 1023 (5V) a 3.) 647 = int(0.632*1023)  
-------------------------
 */ 



volatile int poc_Tv	= 0;
// port D
#define  pinA 5		// skok 5,0V, resp. PWM 
#define  pinB 4		// vybijanie kondenzatora 



#include "p_f_1.h"

volatile int poc_time = 0;	// prirastok 10ms
volatile int y_C = 0;  // u_C = y_C;  
 
volatile unsigned char flag_Tv = 0;
                           
//volatile unsigned char poc_T_vypis = 1; // vypis aj v nultej vzorke
//volatile unsigned char flag_Vypisov = 0;	


//               0        10        20        30
//               01234567890123456789 1234567890123
char Riadok[]= {"                                  "};	
	 


int main(void){   
	/*RC clen R = 10kOhm, C = 47 uF

	*/
	// úvodná inicializácia
	set_bit(DDRD,pinA);		// output
	clear_bit(PORTD,pinA);		// LOW
	set_bit(DDRD,pinB);		// output
	clear_bit(PORTD,pinB);		// LOW
	//x ini_PWM();    // Ini PWM
	
	
	 
	ini_TC2(); // Tv_zaklad = 2ms

	adc_init();
	/* Konfiguracia UART:Tr */
	ini_USART0(MYUBRR);		// 115200Bd
    
    sei();                                      // Enable interrupts in general

  	//  sprintf(Riadok,"RC clen Prev. charak. \r" ); 	//
	// zob_text_UART(Riadok);	
     
    while (1) {
        /* main loop */
		
		
	// generator w_zel
		if (poc_time == 100 ){  // 1. sekunda
			clear_bit(DDRD,pinB); // input, "odstranim skrat"
			set_bit(PORTD,pinA); // HIGH, "zacnem nabijat kondenzator"
			//x-1	OCR0B   = ???;
			
		}
					
		
		/*if(flag_Vypisov){
			flag_Vypisov = 0;
			Vypis();	
		}*/
		
		if((flag_Tv)&&(poc_time <400 )){// 400 * Tv = 4s; 5*T + T + 1s 
			flag_Tv = 0;
		Vypis();		
		} 
				       										
		  
}
}
/*
 * p_f_1.c
 *
 * Created: 5/4/2021 12:49:24 PM
 *  Author: Admin
 */ 

#include <avr/io.h>
#include "p_f_1.h"


void ini_PWM(void){
    // Ini PWM
    OCR0B   = 0;
    TCCR0A  = ???;           // Turn output off when TCNT0 >= OCR0B
                         // Select Fast PWM Mode         
    TCCR0B  = ???;    // fopak = cca xkHz
    DDRD    |= ???;		                // output  PD5	
}

void adc_init(void){
	ADMUX = ???;	// AVCC - nastavenie zdroja ref. napatia
	ADCSRA = ???;	// "zapnutie" ADC
             		// nastavenie preddelica
			 // fADC = 125kHz
			 // trvanie jedneho prevodu cca 0,1ms
			 // zarovnanie doprava
	ADMUX = (ADMUX & 0xF8);	// nastavenie kanalu AD0
	ADCSRA |= (1<<ADSC)|(1<<ADIE);		// spustenie prevodu, povolenie prerusenia od AD
	
}


/*
uint16_t adc_read(uint8_t a_pin){
	a_pin &= 0x07;
	ADMUX = (ADMUX & 0xF8)|a_pin;
	ADCSRA |= (1<<ADSC);		// spustenie prevodu
	while(ADCSRA & (1<<ADSC)); // pockam na dokoncenie prevodu
	return (ADC);
}
*/

ISR(ADC_vect){
	// precitanie AD prevodu kanal 0;
	// ak treba tu nastavíme dalsi kanal 
	// a spustíme az v preruseni od TC2 
	// a vycitame posledne ukonceny prevod
	y_C = ADC; // precitanie AD0
}

void ini_TC2(void){
	//  Nastavenie TC2
	// 7 6         5 4         3      2     1 0
	// COM2A[1:0]  COM2B[1:0]				WGM2[1:0]
	TCCR2A = ???;	// OC2B  PWM mod = 7
	// 7 6 5 4  3      2   1   0
	//          WGM02  CS0[2:0]
	TCCR2B = ???;	// fosc/128
	
	OCR2A = OCR2A_f_opak_TC2; // nastavenie frekvencie opakovania na 2ms
	TIMSK2  = ???;                       // Enable interrupts @ overflow TC2 MOD 7
   
}

ISR(TIMER2_OVF_vect)                           
{ // tato slucka sa vykona kazde 2,0ms
 
    OCR2A = OCR2A_f_opak_TC2;	
	set_bit(ADCSRA,ADSC);//   spustenie dalsieho prevodu ADSC = TRUE
	
	/*
	// nastavim priznak vypisov 
		flag_Vypisov = 1;			// nastavim priznak vypisov.
        //	poc_T_vypis, con_T_vypis;	
	
	*/
	
	if (!poc_Tv)
	{	flag_Tv = 1;			// priznak vypoctu PI reg.
        	poc_time++;
		poc_Tv = con_Tv;		
	} 
	else poc_Tv--;
		
}

void USART_Transmit( uint8_t data ){
	/* Wait for empty transmit buffer */
	while ( !( UCSR0A & (1<<UDRE0)) );
	/* Put data into buffer, sends the data */
	UDR0 = data;
}


// Inicializacia UARTu
void ini_USART0(unsigned int mybr){
	UBRR0 = mybr; 
	set_bit(UCSR0A,U2X0);
	set_bit(UCSR0B,TXEN0);	// Enable TX
	//set_bit(UCSR0B,RXEN0);	// Enable RX
	//set_bit(UCSR0C,USBS0);
	set_bit(UCSR0C,UCSZ01);
	set_bit(UCSR0C,UCSZ00);
}



void zob_text_UART(char *s){
	register uint8_t c;
	while((c = *s++))USART_Transmit(c); // retazec konci "nulou"
}

void Vypis(void){ 
	
	// nap. na kondenzatore,0,63% ustalenej hodnoty, ustalena hodnota (5.0V) 
	sprintf(Riadok,"%d,%d,%d\r\n",y_C, y_C(T),y_C(oo) ); 	// ako dekadicke hodnoty
		  
	zob_text_UART(Riadok);
}
/*
 * p_f_1.h
 *
 * Created: 5/4/2021 12:49:53 PM
 *  Author: Admin
 */ 


#ifndef P_F_1_H_
#define P_F_1_H_


#include <avr/interrupt.h>                      
#include <stdio.h>

#define F_CPU 16000000UL

#define con_Tv  5-1		// Tv = 10 ms  (5 * 2ms)
//#define con_T_vypis 	5-1					// vypis  kazdych 10ms

#define BAUD 115200		
// Nastavenie tejto BaudRate predpoklada 
// UCSR0A.U2X0 = 0
#define MYUBRR F_CPU/8/BAUD-1

void adc_init(void);
//uint16_t adc_read(uint8_t);
void ini_TC2(void);
void ini_PWM(void);

void USART_Transmit( uint8_t );
void ini_USART0(unsigned int );
void zob_text_UART(char * );
void Vypis(void);



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




#define f_opak_TC2 500		// 500 Hz -> 2ms
#define N_D_TC2 128			// Delic = 128
#define OCR2A_f_opak_TC2 F_CPU/f_opak_TC2/N_D_TC2 - 1		// Vysledkom je 2ms casova vzorka !!!!!

extern volatile unsigned char poc_T_vypis; // vypis aj v nultej vzorke
extern volatile unsigned char flag_Vypisov;	
extern volatile unsigned char flag_Tv ;
extern volatile int poc_Tv;

extern volatile int poc_time;

extern volatile int y_C;
extern char Riadok[];	







#endif /* P_F_1_H_ */
/* Arduino code az niekedy inokedy */


RC-SerialPlot01.png
Výstup zo “SerialPtot“ by mohol vyzerať takto.


Zdrojové program upravíme tak, aby sme mohli regulovať napätie na kondenzátore. Najskôr pomocou P regulátora. Na začiatku treba povedať, že vstupom regulátora sú w_{zel} a y_C v rozsahu 0 až 1023. Výstup (plnenie PWM signálu) je z rosahu (0 až 255)). To znamená, že ak chceme nastaviť zosilnenie P regulátora na hodnotu K_P, musí tomu odpovedať rovnica:


          m_{reg} (nT_v) =  (   e(nT_v) K_P )/4;
kde  4 = 1024/256 .

Regulačnú odchýlku počítame podľa vzťahu 
         e(nT_v) = w_{zel}(nT_v) - y_C(nT_v);

Samoyrejme nesmieme zabudnúť na obmedzenie akčného zásahu (min = 0 a max = 255).

Na “pozadí” beží spojitý čas timeVal a my z neho využívame len disktretne vzorky, teda 
        timeVal = n*T_v =  poc_{time}*T_v.

V programe použíjeme namiesto n premennú poc_time typu int.

Môžeme to povedať, aj tak, že regulovaný system mimo týchto okamžikov pracuje “v otvorenej slučke”. Perióda vzorkovania T_v musí byť dostatočne malá, aby sa sta systému medzi vzorkami zmenil len nepatrne. V zhode s prednáškou vyhovuje  T_v = 10 ms. Ak by sme použili PI, resp. PS regulátor, bolo by vhodné nastaviť samostatnú periódu vzorkovania pre zbiehanie PS algoritmu a samostatnú pre výpis priebehu regulovanej veličiny, akčného zásahu, želanej veličiny,...


RC-SerialPlot02.png
Výstup zo “SerialPtot“ pre P regulátor so zosilnením 5 by mohol vyzerať takto.


/*
 * P regulator.c
 * Použijeme SerialPlott-er
 --------------------------
* Program pre vykreslenie regulovanej veliciny, akc. zasahu a zelanej veliciny RC regulovaneho systemu s P regulatorm
* Použite 47uF  kondezátor
* pinA  (D5) je vždy zapojený cez 10kOhm rezistor ako OUTPUT. 
* pinB  (D4) je najskôr zapojený ako output a následne nastavený do log. nuly. 
* pinB a A0 sú skratnuté.
* za cca 2sekundu (delay(2000ms)) po resete bude kondenzátor urcite vybitý. Potom sa
* pinB prepne do stavu input bez pullup a kondenzátor sa zacne nabíjat.
* Vykreslovanie skoncime v case 10 s. 
*
* Vykreslovanie  na SerialPlott-er sa deje v diskrétnom case. Kazdych n*Tv (n = 0, 1, 2, ...), 
* kde Tv je perióda vzorkovania, tu 10 ms
* Vykreslovanie je v pomerných jednotkách. 1SJ = 1023 (10b-ový prevodník).
* 1SJ odpovedá napätiu 5V
* Vykreslujú sa tri ciary:
* 1.) y_c, 2.) u_reg  a 3.) w_zel  
------------------------
 * Created: 4/30/2021 9:26:11 AM
 *  Author: Admin
 */ 


// port D
#define  pinA 5		//  PWM u_reg (D5)
#define  pinB 4		// vybijanie kondenzatora (D4)

volatile int poc_Tv = 0;
volatile int poc_time = 0;	// prirastok 10ms
volatile unsigned char flag_Tv = 0;

#include "p_f_1_P_reg.h"



//volatile unsigned char poc_T_vypis = 1; // vypis aj v nultej vzorke
//volatile unsigned char flag_Vypisov = 0;	

volatile int y_C = 0;  // u_C = y_C;  
int e_reg = 0;
int w_zel = 0;
int u_reg = 0;
int K_P = ???;		// P zlozka regulatora: 1,2,3


//               0        10        20        30
//               01234567890123456789 1234567890123
char Riadok[]= {"                                  "};	
	 

int main(void){   

	// úvodná inicializácia
	set_bit(DDRD,pinA);		// output
	clear_bit(PORTD,pinA);		// LOW
	set_bit(DDRD,pinB);		// output
	clear_bit(PORTD,pinB);		// LOW
	ini_PWM();    // Ini PWM
	ini_TC2(); // Tv_zaklad = 2ms
	adc_init();
	/* Konfiguracia UART:Tr */
	ini_USART0(MYUBRR);		// 115200Bd
    
    	sei();                          // Enable interrupts in general

	//  sprintf(Riadok,"P regulator RC sustavy \r" ); 	//
	// zob_text_UART(Riadok);	
     
    while (1) {
        /* main loop */
			
	// generator w_zel
	if (poc_time<1000){ // 10s = 1000*Tv
			if (poc_time<200) w_zel = 0; else w_zel=800;
			if (poc_time>600)w_zel = 600;
			if (poc_time == 100 ){  // 1. sekunda 100*10ms
				clear_bit(DDRD,pinB); // input, "odstranim skrat"	
		    }
			
			/*if(flag_Vypisov){
			 flag_Vypisov = 0;
			 Vypis();	
			}*/
		
			if(flag_Tv){ 
				flag_Tv = 0;
				P_reg();
				Vypis();
		    }  	
		}				  
 } 
}
/*
 * p_f_1.h
 *
 * Created: 5/5/2021 11:32:45 AM
 *  Author: Admin
 */ 


#ifndef P_F_1_H_
#define P_F_1_H_

#include <avr/interrupt.h>                      
#include <stdio.h>

#define F_CPU 16000000UL

#define con_Tv  5-1		// Tv = 10 ms  (5 * 2ms)
//#define con_T_vypis 	5-1	// vypis  kazdych 10ms

#define BAUD 115200		
// Nastavenie tejto BaudRate predpoklada 
// UCSR0A.U2X0 = 0
#define MYUBRR F_CPU/8/BAUD-1

void adc_init(void);
//uint16_t adc_read(uint8_t);
void ini_TC2(void);
void ini_PWM(void);

void ini_USART0(unsigned int );
void USART_Transmit( uint8_t );
void zob_text_UART(char * );
void Vypis(void);
void P_reg(void);



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



#define f_opak_TC2 500		// 500 Hz -> 2ms
#define N_D_TC2 128			// Delic = 128
#define OCR2A_f_opak_TC2 F_CPU/f_opak_TC2/N_D_TC2 - 1		// Vysledkom je 2ms casova vzorka !!!!!

extern volatile unsigned char poc_T_vypis; // vypis aj v nultej vzorke
extern volatile unsigned char flag_Vypisov;	
extern volatile unsigned char flag_Tv ;
extern volatile int poc_Tv;

extern volatile int poc_time;

extern volatile int y_C;
extern int K_P;		// P zlozka regulatora
extern int w_zel;
extern int u_reg;
extern int e_reg;

extern char Riadok[];	




#endif /* P_F_1_H_ */
/*
 * p_f_1_P_reg.c
 *
 * Created: 5/5/2021 11:32:13 AM
 *  Author: Admin
 */ 


#include <avr/io.h>
#include "p_f_1_P_reg.h"


void ini_PWM(void){

    // Ini PWM
    OCR0B   = 0;
    TCCR0A  = ???;            // Turn output off when TCNT0 >= OCR0B
                         // Select Fast PWM Mode         
    TCCR0B  = ???;    // fopak = cca ?kHz
    DDRD    |= ???;		                // output  PD5	
}

void adc_init(void){
	ADMUX = ???;	 // AVCC - nastavenie zdroja ref. napatia
	ADCSRA = ???;	 // "zapnutie" ADC
             		 // nastavenie preddelica
			 // fADC = 125kHz
			 // trvanie jedneho prevodu cca 0,1ms
			 // zarovnanie doprava
	ADMUX = (ADMUX & 0xF8);	// nastavenie kanalu AD0
	ADCSRA |= (1<<ADSC)|(1<<ADIE);		// spustenie prevodu, povolenie prerusenia od AD
	
}


/*
uint16_t adc_read(uint8_t a_pin){
	a_pin &= 0x07;
	ADMUX = (ADMUX & 0xF8)|a_pin;
	ADCSRA |= (1<<ADSC);		// spustenie prevodu
	while(ADCSRA & (1<<ADSC)); // pockam na dokoncenie prevodu
	return (ADC);
}
*/

ISR(ADC_vect){
	// precitanie AD prevodu kanal 0;
	// ak treba tu nastavíme dalsi kanal 
	// a spustíme az v preruseni od TC2 
	// a vycitame posledne ukonceny prevod
	y_C = ADC; // precitanie AD0
}

void ini_TC2(void){
	//  Nastavenie TC2
	// 7 6         5 4         3      2     1 0
	// COM2A[1:0]  COM2B[1:0]				WGM2[1:0]
	TCCR2A = ???;	// OC2B  PWM mod = 7
	// 7 6 5 4  3      2   1   0
	//          WGM02  CS0[2:0]
	TCCR2B = ???;	// fosc/128
	
	OCR2A = OCR2A_f_opak_TC2; // nastavenie frekvencie opakovania na 2ms
	TIMSK2  = (1<<TOIE2);                       // Enable interrupts @ overflow TC2 MOD 7
   
}

ISR(TIMER2_OVF_vect)                           
{ // tato slucka sa vykona kazde 2,0ms
 
    OCR2A = OCR2A_f_opak_TC2;	
	set_bit(ADCSRA,ADSC);//   spustenie dalsieho prevodu ADSC = TRUE
	
	/*
        // Nastavim flag_Vypisov = 1; kazdych 10ms
	poc_T_vypis, con_T_vypis	
	
	*/
	
	if (!poc_Tv)
	{	flag_Tv = 1;			// priznak vypoctu P reg.
        poc_time++;
	    poc_Tv = con_Tv;		
	} 
	else poc_Tv--;	
}

void USART_Transmit( uint8_t data ){
	/* Wait for empty transmit buffer */
	while ( !( UCSR0A & (1<<UDRE0)) );
	/* Put data into buffer, sends the data */
	UDR0 = data;
}


// Inicializacia UARTu
void ini_USART0(unsigned int mybr){
	UBRR0 = mybr; 
	set_bit(UCSR0A,U2X0);
	set_bit(UCSR0B,TXEN0);	// Enable TX
	//set_bit(UCSR0B,RXEN0);	// Enable RX
	//set_bit(UCSR0C,USBS0);
	set_bit(UCSR0C,UCSZ01);
	set_bit(UCSR0C,UCSZ00);
}



void zob_text_UART(char *s){
	register uint8_t c;
	while((c = *s++))USART_Transmit(c); // retazec konci "nulou"
}

void Vypis(void){ 
	sprintf(Riadok,"%d,%d,%d\r\n",y_C, w_zel, u_reg ); 		  
	zob_text_UART(Riadok);
}

void P_reg(void){
	//e_reg, w_zel, y_C, K_P, u_reg
	// obmedzenia
	OCR0B  = u_reg ;	
}
/* Arduino code az niekedy inokedy */


Úloha 2:

  1. Realizovať regulator pre K_P = 1, 2,3 a porovnať teoretickú a skutočnú trvalú regulačnú odchýlku.
  2. Ak sa niekomu podarí doplniť aj I zložku regulátora môže pripojiť aj záťať regulovaného systému. Viď. prednáška.

Samozrejme, že podkladové programy nie sú napísané dokonale. Študenti môžu programy upravovať a vylepšovať. Napr.: AntiWindUpReset, Ručné riadenie, Zmena w_zel pomocou potenciometra, …



Literatúra




Návrat na zoznam cvičení...
  1. Pravdepodobne zistíte, že 8-bitovým počítadlom T0 s kryštálom 16 MHz sa vám nepodarí nastaviť preddelič a register OCR na takú kombináciu, aby interval bol naozaj presne 10,00 ms. V rámci presnosti nášho merania to nevadí, ale je to principiálny problém. Buď by sme museli použiť 16-bitové počítadlo T1, alebo zmeniť hodnotu kryštálu napr. na 18,432 MHz, v takom prípade by nám čas vyšiel presne.