PS regulátor
Zo stránky 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;
}