Operácie

PS regulátor

Z SensorWiki

Nasledovný príklad predpokladá, že k mikropočítaču je pripojená sústava tvorená RC členom (R = 10k, C = 50uF). Meraná veličina je napätie na kondenzátore (ADC5, pin 28, na doske konektor X4 vpravo hore), vstup do sústavy je realizovaný PWM výstupom (OC1A, pin15, na doske konektor X5 vpravo hore).

Porucha je modelovaná odporom (R=20k) pripájaným paralelne ku kondenzátoru.

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

#include "pomocne.h"

FILE mystdout_uart = FDEV_SETUP_STREAM(sendchar, NULL, _FDEV_SETUP_WRITE);

int main(void)
{ 	
  	UCSR0B = 0x00;        // Disable RxD and TxD for UART0

	//  Inicializacia
	//  Nie vsetky registre su rozpisane; doplnte
   	inituart();		// inicializacia UARTU
	adc_init();		// inicializacia ADC
 	timer1_init();   	// inicializacia Timer1: PWM
	timer2_init();		// inicializacia Timer2: 1ms vzorky su zakladom 

        stdout = &mystdout_uart; 
	//printf("\x1B[1;1HAD [4]=");	

        sei();                 // Enable ALL interrupts             

	do{  
	  if(vypis){           // Vypis sa realizuje kazdych 100 ms
 	  vypis=0;
          //printf("\x1b[2;1H%04x %04d  %04d %04d %3d.%02d ",plnenie,iii,w_zel,reg_odch_n,(y_reg/100),(y_reg%100));
          printf("%d\r",y_reg/2);
          }
	} while(1);
}

K programu potrebujete aj knižnicu pomocne.h s pomocnými funkciami:

#include <stdio.h>

// Premenne:
extern int K_reg;   
extern int Ti_reg;  

extern volatile long m_P_s,m_P_n,m_I_s,m_I_n;
extern volatile int m_reg;    

extern int w_zel;    
extern int w_zel1,w_zel2;    
extern volatile int reg_odch_n; 
extern volatile int reg_odch_s; 

volatile unsigned char plnenie;
extern volatile unsigned int y_reg;
extern int T_v; // 110ms=109 20ms=19
extern volatile unsigned char t_out_spust_prev;
extern volatile unsigned char t_100ms;
extern volatile unsigned int iii;
extern volatile unsigned char vypis;


// Hlavne funkcie:

extern void PS_reg(void);

extern void adc_init(void);
extern void timer1_init(void);
extern void timer2_init(void);
extern void timer2_ovf_isr(void);

// Pomocne funkcie:
 
extern void inituart(void);
extern int sendchar(char c, FILE *stream);

 // Following calculation assumes that F_CPU is assigned in 'Project/Options'

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

/* definitions for UART control */      
                                        // Valid for ATmega328
#define	BAUD_RATE_REG	        UBRR0   // 
#define	UART_STATUS_REG		UCSR0A  // OK, checked (but also control reg.)
#define	UART_CONTROL_REG_B	UCSR0B  // OK, checked
#define UART_CONTROL_REG_C      UCSR0C  // Added for mode setting
#define	ENABLE_TRANSMITTER_BIT	TXEN0   // OK, checked
#define	ENABLE_RECEIVER_BIT	RXEN0   // OK, checked
#define DATA_REGISTER_EMPTY_BIT UDRE0   // Added, for possible speed-up
#define	TRANSMIT_COMPLETE_BIT	TXC0    // OK, checked
#define	RECEIVE_COMPLETE_BIT	RXC0    // OK, checked
#define	UART_DATA_REG		UDR0    // OK, checked

#define SETBIT(VAR,BIT)			((VAR)|=(1<<(BIT)))

A samozrejme aj samotné funkcie (pomocne.c):

//#define  epsilon_0 1
#include <avr/io.h>
#include <avr/interrupt.h>  
#include "pomocne.h"

volatile unsigned char t_out_spust_prev;
volatile unsigned char t_100ms;
volatile unsigned int iii=0;
volatile unsigned char vypis=0;


// Z PSD regulatora je implementovany len PS regulator
// Vynechana je cast Rucne/Automaticka prevadzka (len automaticka)
// Vynechany je ARW
// Vynechane je zadavanie parametrov
// Vynechany je watchdog,...

// Pri vypocte akcneho zasahu je pouzita celociselna aritmetika
// Parametre a vveliciny su normovane takto:
// Zosilnenie regulatora je vynasobene 10-timi, to znamena, ze Kreg=15 predsravuje zosilnenie 1.5
// Integracna casova konstanta je vynasobena 1000. To znamena Ti_reg=500 predstavuje casovu konst. 0.5 sekundy
// Pozadovana hodnota w_zel je vynasobena 100-mi. To znamena w_zel1=450, predstavuje 4.5 V-ta.
// podobne to plati aj pre meranu velicinu.
// akcny zasah m_reg je z rozsahu 0 az % V.  Tato velicina je prepocitana na Plnenie (8-bit)

int K_reg=10;   
int Ti_reg=500;    
volatile long m_P_n=0,m_P_s=0,m_I_s=0,m_I_n=0;

volatile int m_reg= 0;    
int w_zel= 0;    
int w_zel1=400,w_zel2=100;    
volatile int reg_odch_n= 0; // velicina vypocitana v kroku (n*T_v)
volatile int reg_odch_s= 0; // velicina vypocitana v kroku ((n-1)*T_v)

volatile unsigned char plnenie=0;
volatile unsigned int y_reg;
int T_v= 19; // 110ms=109 20ms=19

/*  PS regulator */

void PS_reg(void)
{	
     
	 // vyposuvanie velicin
	 reg_odch_s = reg_odch_n;
	 m_I_s      = m_I_n;
     m_P_s      = m_P_n;

	 // vypocet reg. odchylky
	 reg_odch_n = w_zel - y_reg;

	// Algoritmus PS regulatora
	// m(n*T_v)= m_P((x)*T_v)+ m_I((x)*T_v)
	// x predstavuje krok (n*T_v) resp. ((n-1)*T_v)
    // Vypocet P zlozky regulatora
	 m_P_n = reg_odch_n * K_reg; 
	   if(Ti_reg)
	   {  		  
		  // Vypocet I zlozky regulatora

	      m_I_n += (m_P_s*T_v)/Ti_reg;
		  // co by sa dialo ak by sme predchadzajuci riadok zapisali takto?
		  //m_I_n += (m_P_s/(Ti_reg))*T_v;

	   }
	   else
	   { // nechceme delit nulou
	     m_I_n = 0;
       }
	   
//  epsilon vyjadruje posunutie IC zapojenie na vystupe regulatora
//  epsilon je z intervalu (0, 1>
//  tu uvazujeme lem krajne hodnoty
// epsilon = 0, znamena: po vypocitani sa zmeni hodnota akcneho zasahu okamzite
// epsilon = 1, znamena: hodnota akcneho zasahu sa zmeni az v dalsom kroku

#ifdef epsilon_0
//  vystup posunuty o krok
	m_reg = (m_P_s + m_I_s)/10;
#else 
//  vystup neposunuty; vypocitam a poslem
	m_reg = (m_P_n + m_I_n)/10;
#endif		  
	   
	 
	   
		    
	  // obmedzenie   akcneho zasahu na interval <0 az 5 V>
 	   if(m_reg>500)
	   {
	     m_reg=500; // 5.00 V
	   }
	   if(m_reg < 0)
	   {
	    m_reg=0;
	   }
	  // Doplnte vypocet, ako sme dostali cislo 2?
 	  plnenie =(unsigned char) ((m_reg)/2);
	  OCR1A = plnenie;
	

}



// pouziva sa pri generovani timeout-ov
// pretecenie kazdu 1024 us = 1.0ms (preddelic nastaveny na fosc/64)
//#pragma interrupt_handler timer2_ovf_isr:iv_TIM2_OVF
ISR (TIMER2_OVF_vect)
//void timer2_ovf_isr(void)
{
 TCNT2 = 0x83; //reload counter value

				
	if (t_out_spust_prev) t_out_spust_prev--;
	else 
	{	t_out_spust_prev=T_v-1; // Tv = 110 ms = 109

		SETBIT(ADCSRA,ADSC);//   spustenie dalsieho prevodu ADSC = TRUE
		
	// rampa + meranie prechodovej charakteristiky
	//	if((iii++) <256) OCR1A=iii; else OCR1A=0x0;
	
	}

// generator priebehu zelanej veliciny
	if(t_100ms)t_100ms--;
	else
	{t_100ms = 99; // nastav pocitadlo 100 ms
	  if(iii++==50 )w_zel=w_zel1;
	  if(iii==100 )w_zel=w_zel2;	
	  if(iii==150 )w_zel=w_zel1;	
	  if(iii==200 )w_zel=w_zel2;	
	  if(iii==250 )w_zel=w_zel1;	
	  if(iii==300 )w_zel=w_zel2;	

	vypis=1; // kazdych 0.1 sekundy
 }	
	
}

// ************************************************************	
// prerusenie od A/D prevodnika		
// ************************************************************	
ISR(ADC_vect)
{  
  long pom;
  //conversion complete, read value (int) using...
  // value=ADCL;            //Read 8 low bits first (important)
  // value|=(int)ADCH << 8; //read 2 high bits and shift into top byte

    pom  = ADC;
    pom *= 500;
    pom /= 1024;
  y_reg  = pom; // nanormovane na <0.00 az 5.00)
		
   if (y_reg>500) 
       y_reg=500;
			
    PS_reg();
}



//TIMER2 initialize - prescale:128
// WGM: Normal
// desired value: 1mSec
// actual value:  1,000mSec (0,0%)
void timer2_init(void)
{
 TCCR2B = 0x00; //stop
 ASSR  = 0x00; //set async mode
 TCNT2 = 0x83; //setup
 OCR2A = 0x7D;
 OCR2B = 0x00;
 TCCR2A = 0x00; 
 TCCR2B = 0x05; //start
 TIMSK2 = 0x01;            // TOVIE2 = 1; Enable T2 overflow interrupt         
}

//ADC initialize
// Conversion time: 104uS
void adc_init(void)
{
 ADCSRA = 0x00; //disable adc
 ADMUX = 0x44;//b0100 0100;  //select adc input 4, Uref= 5,0V
//ADMUX     b7  b6  b5  b4     b3  b2  b1  b0
//                             ______________
//                                   MUX      = 0b0100 => 4. kanal
//                      0 
//                 ____
//                 Right adjust = 0 
//          ______
//           Refs  = 01 => Uref = AVCC
 ACSR  = 0x80; // odpojenie Analog. komparator

// ADCSRB = 0x00;
 ADCSRA = 0x8F;
//ADCSRA    b7  b6  b5  b4     b3  b2  b1  b0
//                                ___________
//                                   ADPS      = 0b111 =>  Division Factor =128 => 16MHz/128 = 125 kHz
//                            ____
//                            ADIE = 1 => povolenieprerusenia
//                     ____
//                     ADIF = 1 => vynulovanie prerusenia
//                  0 
//             ____
//             ADSC = 1 => spustenie prevodu
//         ____
//         ADEN  = 01 => pripojenie AD na napatie
 
}

//TIMER1 initialize - prescale:64
// WGM: 5) PWM 8bit fast, TOP=0x00FF
// desired value: 2mSec
// actual value:  1,024mSec (48,8%)
void timer1_init(void)
{
 TCCR1B = 0x00; //stop
 TCCR1A = 0x81;
 TCCR1B = 0x0B; //start Timer
 // Na pin PB1 je pripojeny OC1A FPWM mod
 DDRB|=0x02; 
}

// Funkcie pre obsluhu serioveho komunikacneho rozhrania:

void inituart(void)
{
 BAUD_RATE_REG = (unsigned char)BAUD_PRESCALE;    // Set baud rate: Load the UBRR register

 UART_CONTROL_REG_B = (1 << ENABLE_RECEIVER_BIT)|
                    (1 << ENABLE_TRANSMITTER_BIT); // Enable receive + transmit 

 UART_CONTROL_REG_C = (3<<UCSZ00);                 // Added: Async. UART, None 
                                                   // Parity, 8-data, 1 stopbit
                                                   
}


int sendchar(char c, FILE *stream)
{

  UART_DATA_REG = c;                                   // prepare transmission
  while (!(UART_STATUS_REG & (1 << TRANSMIT_COMPLETE_BIT)));// wait until byte sendt
  UART_STATUS_REG |= (1 << TRANSMIT_COMPLETE_BIT);          // delete TXCflag
  return 0;
}

 int uart_putchar(char c, FILE *stream);

unsigned char recchar(void)
{
  while( !(UART_STATUS_REG & (1 << RECEIVE_COMPLETE_BIT)) );  // wait for data
  return UART_DATA_REG;
}